C++之多层继承、多源继承、菱形继承
本文将通过三个经典示例程序,带你彻底理解:
多层继承的访问规则
多源继承的构造与析构顺序
菱形继承中的二义性问题
一,多层继承
概念
多层继承指 一个类继承另一个派生类,形成继承链,例如:
A → B → C
类 C
间接继承了 A
的成员。
示例代码:
#include <iostream>
using namespace std;class A
{
public:A() { cout << "A" << endl; }~A() { cout << "~A" << endl; }
public:void fun_A_public() { cout << "fun_A_public" << endl; }
protected:void fun_A_protected() { cout << "fun_A_protected" << endl; }
private:void fun_A_private() { cout << "fun_A_private" << endl; }
};class B : public A
{
public:B() { cout << "B" << endl; }~B() { cout << "~B" << endl; }
public:void fun_B_public() { cout << "fun_B_public" << endl; }
protected:void fun_B_protected() { cout << "fun_B_protected" << endl; }
private:void fun_B_private() { cout << "fun_B_private" << endl; }
};class C : public B
{
public:C() { cout << "C" << endl; }~C() { cout << "~C" << endl; }
public:void fun_C_public() { cout << "fun_C_public" << endl; }
protected:void fun_C_protected() { cout << "fun_C_protected" << endl; }
private:void fun_C_private() { cout << "fun_C_private" << endl; }
};int main()
{// 1. 在类外部通过类对象只能访问public成员// 2. 对基类成员的访问由继承方式和原访问权限共同决定// (1)只有基类的public成员,以public形式继承,那么派生来的对象在类外部才可以访问// (2)除此之外的情况,派生来的对象在类外部都无法访问C c;c.fun_A_public();c.fun_B_public();c.fun_C_public();return 0;
}
- 在类的内部,只能访问基类的public、protected成员,private无法访问
- 如果是多层继承的的话,那么子类对基类成员的访问,只看其直接父类中成员访问权限
- 父类成员在子类中存在的的访问权限,是由继承方式和原成员访问权限共同决定
理解方法:
- 如果看子类内部是否能访问,那么将继承关系的代码改为单继承来看。
- 如果看子类外部是否能访问,那么将继承关系的代码改为单个类来看
- 在改代码时,将父类中的代码复制到子类中,然后按照继承方式修改父类成员访问权限
运行结果:
结果分析
构造顺序:
A → B → C
析构顺序:
~C → ~B → ~A
访问权限:
类外部只能访问最终类对象中 public继承下来的public成员。
类内部可访问基类的
public
与protected
,不能访问private
。
二,多源继承
概念
多源继承(或称“多重继承”)是指一个派生类从多个基类继承:
class C : public A, public B
这种设计可以让子类同时拥有多个基类的功能,但也可能带来复杂性。
示例代码:
#include <iostream>
using namespace std;class A
{
public:A(int n) { cout << "A" << endl; }~A() { cout << "~A" << endl; }
};class B
{
public:B(int n) { cout << "B" << endl; }~B() { cout << "~B" << endl; }
};// 多源继承:构造顺序由继承列表顺序决定,而不是初始化列表顺序
class C : public A, public B
{
public:C(int n) : B(n), A(n){cout << "C" << endl;}~C() { cout << "~C" << endl; }
};int main()
{C c(5);return 0;
}
输出结果:
结果分析
构造顺序
由继承列表顺序决定:A → B → C
即使初始化列表写成B(n), A(n)
,也无效。析构顺序
与构造相反:~C → ~B → ~A
注意点
构造顺序只与 类定义时的继承顺序 有关;
初始化列表顺序不会改变这一点;
若多个基类含有同名成员,会产生访问二义性。
三,菱形继承
概念
菱形继承是一种特殊的多重继承结构:
类 D
同时继承 B
和 C
,而 B
、C
又继承自 A
。
这会导致 D
中出现两个 A
子对象,从而引发 二义性问题。
示例代码:
#include <iostream>
using namespace std;class A
{
public:A() { cout << "A" << endl; }~A() { cout << "~A" << endl; }void fun_A() { cout << "fun_A" << endl; }
};class B : public A
{
public:B() { cout << "B" << endl; }~B() { cout << "~B" << endl; }void fun() { cout << "fun_B" << endl; }
};class C : public A
{
public:C() { cout << "C" << endl; }~C() { cout << "~C" << endl; }void fun() { cout << "fun_C" << endl; }
};class D : public B, public C
{
public:D() { cout << "D" << endl; }~D() { cout << "~D" << endl; }
};int main()
{D d;d.B::fun_A(); // 明确指定作用域// d.fun_A(); // 错误:二义性,D 中存在两个 A 子对象cout << sizeof(D) << endl;return 0;
}
运行结果:
结果分析
类
D
拥有 两个独立的 A 子对象(来自 B 和 C)。调用
fun_A()
时会出现二义性错误,需指定作用域:d.B::fun_A()
。内存中
D
的大小包含了两个A
子对象的空间。
解决办法:虚继承
通过在 B
和 C
中使用 虚继承,可以让 D
只保留一个共享的 A
子对象:
class B : virtual public A { ... };
class C : virtual public A { ... };
此时 D
中只有一个 A
实例,二义性问题消失,内存占用也更小。
四、总结对比表