继承与多态:构建类族关系
一、继承体系基础
1.1 派生类语法
class Animal { // 基类
protected:
string name;
public:
void breathe() {
cout << "呼吸中..." << endl;
}
};
class Dog : public Animal { // 公有继承
public:
void bark() {
cout << name << ": 汪汪!" << endl; // 可访问protected成员
}
};
1.2 访问控制矩阵
| 基类成员访问权限 | 继承方式 | 派生类访问权限 |
|---|---|---|
| public | public | public |
| protected | public | protected |
| private | public | 不可访问 |
| public | protected | protected |
| protected | protected | protected |
| private | protected | 不可访问 |
| public | private | private |
| protected | private | private |
| private | private | 不可访问 |
1.3 构造链式调用
class Vehicle {
protected:
int wheels;
public:
Vehicle(int w) : wheels(w) {}
};
class Car : public Vehicle {
string brand;
public:
Car(string b, int w) : Vehicle(w), brand(b) {} // 先初始化基类
};
🔍 内存布局示例:
Dog对象内存结构:
[Animal子对象] → [name]
[Dog扩展部分] → [bark()等]
二、多态实现机制
2.1 虚函数与动态绑定
class Shape {
public:
virtual double area() const { // 虚函数声明
return 0;
}
virtual ~Shape() {} // 虚析构函数!
};
class Circle : public Shape {
double radius;
public:
Circle(double r) : radius(r) {}
double area() const override { // 重写虚函数
return 3.14 * radius * radius;
}
};
void printArea(const Shape& shape) {
cout << "面积: " << shape.area() << endl; // 动态绑定
}
2.2 虚函数表(vTable)原理
对象内存布局:
[虚表指针] → [虚函数地址表]
[成员变量...]
调用过程:
1. 通过对象找到虚表指针
2. 查表获得函数实际地址
3. 执行目标函数
📊 多态性能对比:
| 特性 | 静态绑定 | 动态绑定 |
|---|---|---|
| 决议时机 | 编译期 | 运行期 |
| 性能开销 | 无 | 查表开销 |
| 灵活性 | 低 | 高 |
| 适用场景 | 非虚函数 | 需要多态的行为 |
三、现代C++特性
3.1 override关键字
class Base {
public:
virtual void show(int) const;
};
class Derived : public Base {
public:
void show(int) const override; // 正确重写
// void show() override; // 错误!签名不匹配
};
3.2 final限定符
class NoExtend final { // 禁止继承
//...
};
class Base {
public:
virtual void lock() final; // 禁止重写
};
// class Derived : public NoExtend {}; // 错误!
四、综合应用案例
class PaymentMethod {
public:
virtual ~PaymentMethod() = default;
virtual void process(double amount) = 0; // 纯虚函数
};
class Alipay : public PaymentMethod {
public:
void process(double amount) override {
cout << "支付宝支付:" << amount << "元" << endl;
}
};
class WechatPay : public PaymentMethod {
public:
void process(double amount) override {
cout << "微信支付:" << amount << "元" << endl;
}
};
void processPayment(PaymentMethod& method, double amt) {
method.process(amt); // 多态调用
}
五、关键原则与陷阱
5.1 重要规则
-
切片问题:基类对象接收派生类对象时丢失扩展成员
Derived d; Base b = d; // 发生对象切片 -
虚析构函数:基类必须声明虚析构函数
-
纯虚函数:包含纯虚函数的类成为抽象基类
virtual void func() = 0; // 纯虚函数声明
5.2 类型转换
| 转换方式 | 安全性 | 使用场景 |
|---|---|---|
| static_cast | 编译期检查 | 明确类型关系的向下转型 |
| dynamic_cast | 运行时检查 | 安全的多态类型转换 |
| reinterpret_cast | 无检查 | 低级二进制转换 |
六、总结与提升
课后思考:
- 为什么构造函数不能是虚函数?
- 如何通过基类指针正确释放派生类对象?
实践建议:
- 实现图形系统的类层次:Shape→Circle/Rectangle/Triangle
- 添加序列化功能,验证多态行为
- 使用typeid和dynamic_cast实现类型安全检测
设计原则:
- 遵循里氏替换原则:派生类应完全替代基类
- 优先使用组合而非继承
- 对多态基类声明虚析构函数
- 使用智能指针管理多态对象
通过合理运用继承与多态,可以创建出扩展性强、维护方便的类层次结构。建议结合UML类图进行系统设计,并使用gdb等工具观察虚表指针的变化,深入理解多态的实现机制。