一、对象赋值的双向可行性分析

1.1 基类到派生类的转换

  • 隐式转换: 不允许直接赋值(如 Base b = d;)
  • 显式转换: 需使用构造函数或类型转换运算符
1
2
3
4
5
6
7
8
9
10
11
12
class Base {
public:
Base() {}
explicit Base(int val) : data(val) {}
private:
int data;
};

class Derived : public Base {
public:
Derived(int val) : Base(val) {}
};
  • 内存影响: 派生类对象包含基类数据成员,赋值操作不会改变大小,但会丢失派生类特有数据

1.2 派生类到基类的转换

  • 隐式转换: 允许(如 Base b = d;)
  • 显式转换: 可使用static_cast或构造函数
1
2
3
Derived d;
Base b = d; // 隐式转换
Base& br = static_cast<Base&>(d); // 显式转换
  • 切割现象:
1
2
Base* p = new Derived; // 合法(向上转换)
Base b = *p; // 不合法(会导致切割,丢失Derived特有数据)

二、指针类型转换的上下行约束验证

2.1 向上转换(基类指针指向派生类对象)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Base {
public:
virtual void show() { cout << "Base"; }
};

class Derived : public Base {
public:
void show() override { cout << "Derived"; }
};

int main() {
Derived d;
Base* p = &d; // 合法,向上转换
p->show(); // 调用Derived::show(),多态生效
}
  • 内存布局:
1
2
3
// 假设int占4字节,虚函数表指针占8字节
sizeof(Base) = 8 bytes
sizeof(Derived) = 12 bytes (4字节data + 8字节虚函数表指针)

2.2 向下转换(派生类指针指向基类对象)

  • 直接赋值: 不允许(如 Base b; Derived* d = &b;)
  • 强制转换: 需使用dynamic_cast或reinterpret_cast
1
2
Base b;
Derived* d = dynamic_cast<Derived*>(&b); // 合法,但可能为空
  • 安全限制:
1
2
Base* p = new Derived;
Base* b = new Base; // 不允许直接赋值

三、引用绑定的兼容性边界检测

3.1 向上转换(基类引用绑定派生类对象)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Base {
public:
virtual void show() { cout << "Base"; }
};

class Derived : public Base {
public:
void show() override { cout << "Derived"; }
};

int main() {
Derived d;
Base& br = d; // 合法,向上转换
br.show(); // 调用Derived::show(),多态生效
}

3.2 向下转换(派生类引用绑定基类对象)

  • 直接绑定: 不允许(如 Derived d = b;)
  • 强制转换: 需使用static_cast或reinterpret_cast
1
2
Base b;
Derived& dr = static_cast<Derived&>(b); // 合法,但可能越界
  • 绑定有效性:
1
2
Base* p = new Base;
Derived& dr = *p; // 不合法,导致未定义行为

四、内存特性规律总结

4.1 空继承特殊情况

1
2
class Base {};
class Derived : public Base {}; // 空继承
  • 内存大小: sizeof(Derived) == sizeof(Base)(均占1字节)
  • 内存对齐: 两个类的对齐要求相同(均按1字节对齐)

4.2 一般继承内存规律

1
2
3
4
5
6
7
8
9
10
class Base {
public:
int data;
virtual void show() {}
};

class Derived : public Base {
public:
float extra;
};
  • 内存布局:
    • Base: 4字节data + 8字节虚函数表指针 = 12字节
    • Derived: 4字节data + 8字节虚函数表指针 + 4字节extra = 16字节
  • 对齐规则: 按最大对齐要求(4字节)进行内存对齐

五、类型转换安全性保障

5.1 虚函数表作用

1
2
Base* p = new Derived;
p->show(); // 通过虚函数表实现多态
  • 虚函数表结构: 存储了虚函数地址,确保正确调用派生类重写函数
  • 内存访问: 虚函数表指针保证了正确的类型识别能力

5.2 安全转换实践

1
2
3
// 安全向下转换
Base* p = new Derived;
Derived* d = dynamic_cast<Derived*>(p); // 合法,会检查类型兼容性
  • 类型兼容性: 必须保证目标类型是源类型的公有派生类
  • 空指针处理: dynamic_cast会返回nullptr(当转换失败时)

六、典型场景示例

6.1 正确转换示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Animal {
public:
virtual ~Animal() {}
virtual void sound() { cout << "Animal sound"; }
};

class Dog : public Animal {
public:
void sound() override { cout << "Bark"; }
};

int main() {
Dog d;
Animal* p = &d; // 正确的向上转换
p->sound(); // 正确调用Dog::sound()
Dog* pd = dynamic_cast<Dog*>(p); // 正确的向下转换
}

6.2 错误转换示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Base {
public:
int data;
};

class Derived : public Base {
public:
float extra;
};

int main() {
Base b;
Derived* d = &b; // 错误的向下转换,会导致未定义行为
// 正确做法:
Base* p = new Derived;
Derived* d = static_cast<Derived*>(p); // 正确的向下转换
}

七、关键结论

  1. 转换方向性:

    • 向上转换(基类→派生类):对象赋值、指针指向、引用绑定均可行
    • 向下转换(派生类→基类):只能通过显式转换实现,且存在切割风险
  2. 内存特性规律:

    • 派生类大小至少等于基类大小
    • 空继承时大小相等(但继承关系依然有效)
    • 内存对齐由基类的对齐要求决定
  3. 类型转换安全:

    • 需保证类型兼容性(派生类必须是基类的公有派生类)
    • 使用dynamic_cast确保安全向下转换
    • 避免直接赋值导致的切割现象
  4. 多态机制:

    • 虚函数表是实现多态的关键
    • 指针/引用转换时必须保持虚函数表的连续性
    • 内存布局需考虑虚函数表指针的存储位置