
在 C++ 的面向对象编程中,继承(Inheritance)是实现代码复用和类型扩展的核心机制。我们熟悉的 “单继承”(Single Inheritance)允许一个派生类从一个基类继承属性和方法,但现实中的复杂场景往往需要更灵活的模型 —— 例如,一个 “智能手表” 类可能需要同时继承 “计时器”(Timer)和 “通信模块”(Communication)两个独立的基类。这时,C++ 提供的多重继承(Multiple Inheritance)就能大显身手。
多重继承允许一个派生类同时从多个基类继承特征。其语法非常简单:在派生类声明时,用逗号分隔多个基类,并指定继承权限(public/protected/private)。
// 基类A
class BaseA {
public:
BaseA(int a) : a_(a) {}
void printA() { std::cout << "BaseA: " << a_ << std::endl; }
protected:
int a_;
};
// 基类B
class BaseB {
public:
BaseB(int b) : b_(b) {}
void printB() { std::cout << "BaseB: " << b_ << std::endl; }
protected:
int b_;
};
// 派生类Derived,同时继承BaseA和BaseB(public继承)
class Derived : public BaseA, public BaseB {
public:
// 派生类构造函数需要显式初始化所有基类
Derived(int a, int b, int d)
: BaseA(a), BaseB(b), d_(d) {} // 注意:基类初始化顺序由声明顺序决定
void printD() {
std::cout << "Derived: " << d_
<< " (from BaseA: " << a_ // 继承自BaseA的protected成员
<< ", from BaseB: " << b_ // 继承自BaseB的protected成员
<< ")" << std::endl;
}
private:
int d_; // 派生类新增成员
};场景 | 示例 |
|---|---|
接口实现 | 同时实现多个抽象接口 |
功能组合 | 打印机+扫描仪→多功能一体机 |
代码复用 | 组合多个工具类功能 |
在面向对象中,“状态” 通常指类的成员变量(属性)。多重继承的派生类会分别继承每个基类的成员变量,并在对象内存中为每个基类分配独立的存储空间。
当创建一个派生类对象时,内存中会包含:
以Derived类为例,其对象的内存布局结构图如下图所示:

通过以下代码可以验证派生类对基类成员的继承:
int main() {
Derived d(10, 20, 30);
d.printA(); // 输出:BaseA: 10(继承自BaseA)
d.printB(); // 输出:BaseB: 20(继承自BaseB)
d.printD(); // 输出:Derived: 30 (from BaseA: 10, from BaseB: 20)
return 0;
}运行结果:

派生类Derived成功继承了BaseA和BaseB的成员函数和成员变量。
多重继承中,派生类的构造和析构顺序是最容易出错的环节。理解其规则对避免逻辑错误至关重要。
派生类构造时,基类的构造函数按声明顺序被调用(与初始化列表中的顺序无关)。具体规则如下:
代码示例:验证构造顺序
#include <iostream>
// 基类1
class Base1 {
public:
Base1() { std::cout << "Base1 构造" << std::endl; }
};
// 基类2
class Base2 {
public:
Base2() { std::cout << "Base2 构造" << std::endl; }
};
// 派生类,继承顺序:Base1, Base2
class Derived : public Base1, public Base2 {
public:
Derived() : Base2(), Base1() { // 初始化列表顺序与基类声明顺序相反
std::cout << "Derived 构造" << std::endl;
}
};
int main() {
Derived d;
return 0;
}
无论初始化列表中基类的顺序如何,构造函数始终按派生类声明时基类的顺序调用(Base1→Base2)。初始化列表仅用于传递参数,不影响调用顺序。
析构函数的调用顺序与构造函数完全相反:
代码示例:验证析构顺序
#include <iostream>
class Base1 {
public:
~Base1() { std::cout << "Base1 析构" << std::endl; }
};
class Base2 {
public:
~Base2() { std::cout << "Base2 析构" << std::endl; }
};
class Derived : public Base1, public Base2 {
public:
~Derived() { std::cout << "Derived 析构" << std::endl; }
};
int main() {
Derived d;
return 0;
}
C++ 标准规定,对象的构造是 “自顶向下”(基类→派生类),而析构是 “自底向上”(派生类→基类)。这一设计保证了对象状态的完整性:基类的资源(如内存、句柄)在派生类构造前已准备完毕,在派生类析构后才释放。
多重继承最臭名昭著的问题是 “菱形继承”(Diamond Problem),它会导致派生类中出现基类的多份拷贝,引发歧义(Ambiguity)和资源浪费。
假设存在四个类:A是顶层基类,B和C都继承自A,D同时继承自B和C。类关系如下图 所示:

菱形继承结构(A→B→D,A→C→D)
代码示例:菱形继承的歧义
#include <iostream>
class A {
public:
void func() { std::cout << "A::func()" << std::endl; }
};
class B : public A {}; // B继承A
class C : public A {}; // C继承A
class D : public B, public C {}; // D继承B和C
int main() {
D d;
// d.func(); // 编译错误:'func' is ambiguous(歧义)
return 0;
}
错误分析:D的对象d中包含B和C的子对象,而B和C各自包含A的子对象。因此,d中存在两份A的拷贝(B::A和C::A)。当调用d.func()时,编译器无法确定调用的是B::A::func()还是C::A::func(),导致歧义。
C++ 提供虚继承(Virtual Inheritance)机制,通过声明基类为 “虚基类”(Virtual Base Class),确保菱形结构中顶层基类仅存在一份拷贝。
①虚继承的语法
在派生类声明时,使用virtual关键字修饰基类:
class B : virtual public A {}; // B虚继承A
class C : virtual public A {}; // C虚继承A
class D : public B, public C {}; // D继承B和C(此时B和C的A是同一实例)②虚继承的内存布局
虚继承通过虚基类表(Virtual Base Table)实现。每个包含虚基类的派生类对象会额外存储一个指针(vbptr),指向虚基类表。表中记录了该派生类到虚基类的偏移量,确保所有派生路径共享同一个虚基类实例。
以D为例,其内存布局结构图 :

虚继承后,D 对象中仅包含一份 A 的实例
③代码验证:虚继承消除歧义
修改之前的菱形继承代码,使用虚继承:
#include <iostream>
class A {
public:
void func() { std::cout << "A::func()" << std::endl; }
};
class B : virtual public A {}; // 虚继承
class C : virtual public A {}; // 虚继承
class D : public B, public C {}; // D继承B和C
int main() {
D d;
d.func(); // 正确调用:A::func()(无歧义)
return 0;
}
虚继承会改变构造函数的调用顺序。在虚继承链中,虚基类的构造函数由最终派生类直接调用,且仅调用一次。具体规则如下:
①代码示例:虚继承的构造顺序
#include <iostream>
class A {
public:
A() { std::cout << "A 构造" << std::endl; }
};
class B : virtual public A { // 虚继承A
public:
B() { std::cout << "B 构造" << std::endl; }
};
class C : virtual public A { // 虚继承A
public:
C() { std::cout << "C 构造" << std::endl; }
};
class D : public B, public C {
public:
D() { std::cout << "D 构造" << std::endl; }
};
int main() {
D d;
return 0;
}
虚基类A的构造函数由最终派生类D直接调用,且仅调用一次(即使B和C都继承了A)。这确保了虚基类在整个继承链中只存在一份实例。
d.BaseA::func())。class Drawable { virtual void draw() = 0; }; // 可绘制接口
class Clickable { virtual void onClick() = 0; }; // 可点击接口
class Button : public Drawable, public Clickable { // 按钮类实现两个接口
void draw() override { /* 绘制逻辑 */ }
void onClick() override { /* 点击逻辑 */ }
};QObject)允许通过多重继承扩展功能(但需注意虚继承的使用)。 如果多个基类的关系是 “拥有” 而非 “是”(Is-A),应优先使用组合(Composition)。例如,“汽车” 类需要 “引擎” 和 “变速箱”,更合理的设计是:
class Engine { /* ... */ };
class Gearbox { /* ... */ };
class Car {
private:
Engine engine_;
Gearbox gearbox_;
};建议多重继承的基类数量不超过 2-3 个。更多基类会显著增加代码复杂度。
当多个基类存在同名成员时,使用作用域解析符(::)显式指定:
class BaseA { public: void func() {} };
class BaseB { public: void func() {} };
class Derived : public BaseA, public BaseB {
public:
void callFunc() {
BaseA::func(); // 调用BaseA的func()
BaseB::func(); // 调用BaseB的func()
}
};虚继承虽然解决了菱形问题,但会增加内存开销和构造顺序的复杂度。仅在明确需要共享基类实例时使用(如接口类)。
多重继承是 C++ 中强大但复杂的特性,它允许类同时继承多个基类的状态和行为,但也带来了构造顺序、菱形继承等挑战。通过本文的学习,我们掌握了以下核心点:
知识点 | 关键结论 |
|---|---|
状态继承 | 派生类继承每个基类的独立子对象,内存中为每个基类分配空间。 |
构造 / 析构顺序 | 构造按基类声明顺序,析构按逆序;虚继承时虚基类由最终派生类直接构造。 |
菱形继承问题 | 导致基类多份拷贝,引发歧义;虚继承通过共享实例解决此问题。 |
最佳实践 | 优先组合、限制基类数量、显式处理冲突、谨慎使用虚继承。 |
最后,多重继承的合理使用需要开发者对类关系有清晰的设计。在大多数场景下,单继承 + 组合已足够;仅当需要表达明确的 “多角色” 关系(如接口实现)时,才应选择多重继承。
#include <iostream>
class Base1 {
public:
Base1(int a) : a_(a) { std::cout << "Base1 构造,a_ = " << a_ << std::endl; }
~Base1() { std::cout << "Base1 析构" << std::endl; }
protected:
int a_;
};
class Base2 {
public:
Base2(int b) : b_(b) { std::cout << "Base2 构造,b_ = " << b_ << std::endl; }
~Base2() { std::cout << "Base2 析构" << std::endl; }
protected:
int b_;
};
class Derived : public Base1, public Base2 {
public:
Derived(int a, int b, int d)
: Base1(a), Base2(b), d_(d) { // 基类初始化顺序由声明顺序决定(Base1→Base2)
std::cout << "Derived 构造,d_ = " << d_ << std::endl;
}
~Derived() { std::cout << "Derived 析构" << std::endl; }
private:
int d_;
};
int main() {
std::cout << "--- 创建Derived对象 ---" << std::endl;
Derived d(10, 20, 30);
std::cout << "\n--- 销毁Derived对象 ---" << std::endl;
return 0;
}输出结果:

#include <iostream>
class A {
public:
A() { std::cout << "A 构造" << std::endl; }
void func() { std::cout << "A::func()" << std::endl; }
};
class B : virtual public A { // 虚继承A
public:
B() { std::cout << "B 构造" << std::endl; }
};
class C : virtual public A { // 虚继承A
public:
C() { std::cout << "C 构造" << std::endl; }
};
class D : public B, public C {
public:
D() { std::cout << "D 构造" << std::endl; }
};
int main() {
D d;
d.func(); // 无歧义调用A::func()
return 0;
}输出结果:
