17、继承:单继承、多继承、继承方式(public、protected、private)、菱形继承问题
继承,说白了就是让一个类「拥有」另一个类的特性。我刚开始学C++时,觉得这东西不就是抄代码吗?后来在Qt项目里写自定义控件,才发现继承是代码复用的灵魂。没有它,你每个按钮都得重写一遍绘制逻辑,那画面太美我不敢看。
17.1 单继承:最朴素的复用
单继承就是一个子类只继承一个父类。这是最常用的方式,也是我建议初学者优先掌握的。
class Animal {
public:
void eat() { qDebug() << "吃东西"; }
};
class Dog : public Animal {
public:
void bark() { qDebug() << "汪汪"; }
};
你看,Dog 类没写 eat 方法,但它能用。这就是继承的威力。我在Qt里写自定义窗口时,经常这样从 QWidget 派生,省掉大量重复代码。
17.2 多继承:一把双刃剑
C++支持一个子类继承多个父类。听起来很强大,但用不好就是灾难。
class Teacher {
public:
void teach() { qDebug() << "教书"; }
};
class Researcher {
public:
void research() { qDebug() << "科研"; }
};
class Professor : public Teacher, public Researcher {
// 同时拥有 teach 和 research 方法
};
多继承在Qt的信号槽机制里也有体现。比如 QPushButton 继承自 QAbstractButton,而 QAbstractButton 又继承自 QWidget。但Qt官方建议尽量少用多继承,我深表赞同。
17.3 继承方式:public、protected、private
继承方式决定了父类成员在子类中的访问权限。我画了个表,方便你理解:
| 继承方式 | 父类public成员 | 父类protected成员 | 父类private成员 |
|---|---|---|---|
| public继承 | public | protected | 不可访问 |
| protected继承 | protected | protected | 不可访问 |
| private继承 | private | private | 不可访问 |
你想想看,public继承是最常用的,它表达的是「is-a」关系——子类是一种父类。protected继承和private继承用得少,但也不是没用。我在封装第三方库时,就喜欢用private继承,把不想暴露的接口藏起来。
17.4 菱形继承问题:C++的经典噩梦
菱形继承长这样:
class A {
public:
int data;
};
class B : public A {};
class C : public A {};
class D : public B, public C {};
D 类里会有两份 A 的 data 成员。你调用 d.data 时,编译器会懵——你到底要 B 的那份还是 C 的那份?
我在一个图形引擎项目里遇到过这个问题。当时有个 Shape 基类,Circle 和 Rectangle 都继承它,而 RoundRect 又同时继承 Circle 和 Rectangle。结果 RoundRect 对象里有两份 Shape 的坐标数据,调试时差点把键盘砸了。
解决方案是虚继承:
class A {
public:
int data;
};
class B : virtual public A {};
class C : virtual public A {};
class D : public B, public C {};
加上 virtual 关键字后,D 里只有一份 A 的成员。虚继承的原理是让编译器在对象布局里加一个指针,指向共享的基类部分。代价是性能稍微下降,但换来的是逻辑清晰。
17.5 知识体系总览
下面这张图帮你理清本章的核心脉络:
17.6 避坑指南
- 我曾经犯过的错:在public继承里把父类的析构函数写成非虚函数。结果子类对象被父类指针删除时,子类析构函数没调用,内存泄漏了。记住:只要类可能被继承,析构函数就写成虚函数。
- 另一个教训:多继承时两个父类有同名成员变量,子类访问时编译器报错。我当时的解决办法是加作用域,但后来重构时发现,其实用组合更合适。
- 虚继承的坑:虚基类的构造函数参数传递很麻烦。最派生类要负责初始化所有虚基类,代码维护起来像在走钢丝。我建议你非必要不用虚继承。
嗯,继承这块内容就这些。记住,继承是工具,不是目的。用得好,代码优雅复用;用不好,就是给自己挖坑。多写多练,慢慢就有感觉了。