Q1:什么是多重继承?
多重继承(Multiple lnheritance)是指一个类同时继承多个基类的特性。
class A { };
class B { };
class C : public A, public B { };
这样 c 同时拥有 A和 B的成员。
优点: 代码复用灵活:
缺点:容易引起命名冲突、二义性、以及“菱形继承”问题。
二、命名冲突题
Q2:如果两个基类有相同的成员名,派生类如何访问?
class A {
public:
int x = 10;
};
class B {
public:
int x = 20;
};
class C : public A, public B {
public:
void print() {
// cout << x; // 错误:x 不明确
cout << A::x << " " << B::x << endl;
}
};
答:
直接访问 x会报错(二义性)
必须通过作用域限定符(A::x/ B::x)区分。
三、构造与析构顺序题
Q3:在多重继承中,构造和析构的顺序是什么?
class A { public: A(){cout<<"A ";} ~A(){cout<<"~A ";} };
class B { public: B(){cout<<"B ";} ~B(){cout<<"~B ";} };
class C : public A, public B {
public:
C() { cout<<"C "; }
~C(){ cout<<"~C "; }
};
int main(){
C obj;
}
A B C ~C ~B ~A
✅ 规律:
-
构造顺序: 按继承声明顺序(从左到右) → 自己。
-
析构顺序: 与构造相反(先派生类,再右到左基类)
四、菱形继承问题
什么是菱形继承?为什么会出问题?
class A {
public:
int x = 100;
};
class B : public A { };
class C : public A { };
class D : public B, public C { };
D继承了两次 A→ 存在两份 A::x,出现二义性。
D d;
cout << d.x; // ❌ 二义性错误:不明确是 B::A::x 还是 C::A::x
Q5:如何解决菱形继承的问题?
通过 虚继承(virtual inheritance)
class A {
public:
int x = 100;
};
class B : virtual public A { };
class C : virtual public A { };
class D : public B, public C { };
int main() {
D d;
d.x = 200; // ✅ 唯一的 A
cout << d.x; // 输出 200
}
解析:
虚继承让 B和c共享同一份 A;
D 只保留一份 A 子对象;
常用于多重继承的“公共基类“场景(如 iostream)
六、虚继承下构造顺序题
Q6:虚继承时基类的构造顺序?
class A { public: A(){cout<<"A ";} };
class B : virtual public A { public: B(){cout<<"B ";} };
class C : virtual public A { public: C(){cout<<"C ";} };
class D : public B, public C { public: D(){cout<<"D ";} };
int main(){ D d; }
输出:
A B C D
规律:
-
虚基类
A由最派生类D负责构造; -
构造顺序:
虚基类 → 非虚基类 → 派生类本身。
七、指针类型题
(多继承指针偏移)
多重继承时,基类指针转换到派生类时有什么特点?
class A { public: int a; };
class B { public: int b; };
class C : public A, public B { public: int c; };
int main() {
C obj;
A* pa = &obj;
B* pb = &obj;
cout << pa << endl;
cout << pb << endl;
}
0x0012FF40
0x0012FF44 (地址不同!)
什么是菱形继承(Diamond Inheritance)
菱形继承指:有一个基类 A,两个类 B 与 C 同时继承自 A,然后另一个类 D 同时继承自 B 和 C。类继承关系在图上看起来像一个菱形(或菱形/钻石),因此得名
A
/ \
B C
\ /
D
默认(非虚继承)情况下,D 将包含 两份 A 的子对象:一份来自 B,一份来自 C。这会引发若干问题:
-
数据冗余:
D有两份A的成员变量(占更多内存)。 -
二义性/命名冲突:访问
A的成员时编译器会报二义性(不知道你要哪一份)。 -
指针/转型问题:把
D*转为A*有二义性,不明确要通过哪条路径(B->A 还是 C->A)。 -
不符合语义:在很多设计中我们期望
B和C共享一份A,而不是各自拷贝一份。
#include <iostream>
using namespace std;
class A {
public:
int x;
A() : x(1) { cout << "A() "; }
};
class B : public A {
public:
B() { cout << "B() "; }
};
class C : public A {
public:
C() { cout << "C() "; }
};
class D : public B, public C {
public:
D() { cout << "D() "; }
};
int main() {
D d;
// cout << d.x; // ❌ 编译错误:不明确(B::A::x 还是 C::A::x)
cout << "sizeof(D) = " << sizeof(D) << endl;
}
运行/编译后你会发现:
-
构造顺序会是
A B A C D—— 两次构造A(分别来自B和C)。 -
sizeof(D)会比只有一份A的情况要大(因为有两份A)。
解决办法:虚继承(virtual inheritance)
C++ 提供 虚继承(virtual 关键字)来保证最派生类 D 只包含一份 A,即让 B 和 C 共享同一份 A 子对象。
class B : virtual public A { ... };
class C : virtual public A { ... };
class D : public B, public C { ... };
效果:
-
D中只有一份A子对象(共享)。 -
解决了数据冗余与二义性。
-
构造顺序发生变化:虚基类由最派生类负责构造,并且虚基类构造只调用一次。
#include <iostream>
using namespace std;
class A {
public:
int x;
A(int v=1) : x(v) { cout << "A("<<x<<") "; }
};
class B : virtual public A {
public:
B() { cout << "B() "; }
};
class C : virtual public A {
public:
C() { cout << "C() "; }
};
class D : public B, public C {
public:
D() : A(42) { // 注意:最派生类负责构造虚基类 A
cout << "D() ";
}
};
int main() {
D d;
cout << "\nA.x = " << d.x << endl; // 可以直接访问,只有一份 A::x
}
A(42) B() C() D()
A.x = 42
1293

被折叠的 条评论
为什么被折叠?



