C++面试3_多重继承

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,两个类 BC 同时继承自 A,然后另一个类 D 同时继承自 BC。类继承关系在图上看起来像一个菱形(或菱形/钻石),因此得名

    A
   / \
  B   C
   \ /
    D

默认(非虚继承)情况下,D 将包含 两份 A 的子对象:一份来自 B,一份来自 C。这会引发若干问题:

  1. 数据冗余D 有两份 A 的成员变量(占更多内存)。

  2. 二义性/命名冲突:访问 A 的成员时编译器会报二义性(不知道你要哪一份)。

  3. 指针/转型问题:把 D* 转为 A* 有二义性,不明确要通过哪条路径(B->A 还是 C->A)。

  4. 不符合语义:在很多设计中我们期望 BC 共享一份 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(分别来自 BC)。

  • sizeof(D) 会比只有一份 A 的情况要大(因为有两份 A)。

解决办法:虚继承(virtual inheritance)

C++ 提供 虚继承virtual 关键字)来保证最派生类 D 只包含一份 A,即让 BC 共享同一份 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
 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值