MST

星途 面试题库

面试题:C++继承中多重继承可能带来的弊端及解决方案

在C++的多重继承场景下,可能会出现诸如菱形继承等问题,这些问题会给程序带来哪些弊端?请详细描述,并说明有哪些方法可以解决这些弊端,比如使用虚继承等,同时分析不同解决方案的优缺点。
30.1万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

菱形继承带来的弊端

  1. 数据冗余:在菱形继承结构中,从最派生类访问菱形顶端基类的成员时,会存在数据冗余。例如,若顶端基类有成员变量data,在菱形底部的派生类中会存在多份data的拷贝,浪费内存空间。
  2. 访问歧义:由于存在多份基类成员的拷贝,当在最派生类中访问菱形顶端基类成员时,编译器无法明确知道该访问哪一份拷贝,从而产生访问歧义。

解决方案及优缺点

虚继承

  1. 实现方式:在继承关系中,通过在基类前加上virtual关键字来声明虚继承。例如:
class Base {
public:
    int data;
};
class Derived1 : virtual public Base {};
class Derived2 : virtual public Base {};
class Final : public Derived1, public Derived2 {};
  1. 优点
    • 解决了数据冗余问题,在最派生类中,菱形顶端基类成员只保留一份拷贝。
    • 消除了访问歧义,因为只有一份基类成员,编译器不会出现不知道访问哪一份拷贝的情况。
  2. 缺点
    • 实现复杂,编译器需要额外维护虚基类表等结构,增加了内存开销和运行时开销。
    • 虚继承可能会使代码可读性变差,尤其是在复杂的继承体系中。

接口继承

  1. 实现方式:将菱形顶端基类定义为纯接口类(包含纯虚函数的抽象类),具体实现由中间派生类各自完成。例如:
class Interface {
public:
    virtual void func() = 0;
};
class Derived1 : public Interface {
public:
    void func() override { /* 具体实现 */ }
};
class Derived2 : public Interface {
public:
    void func() override { /* 具体实现 */ }
};
class Final : public Derived1, public Derived2 {};
  1. 优点
    • 避免了数据冗余问题,因为纯接口类不包含数据成员。
    • 减少了访问歧义,因为具体实现由中间派生类负责,各自明确。
  2. 缺点
    • 对于需要共享数据和实现的场景不适用,因为纯接口类不能有数据成员和实现代码。
    • 增加了代码的复杂度,需要在中间派生类中重复实现一些逻辑。

组合方式

  1. 实现方式:在派生类中,通过组合的方式包含菱形顶端基类对象,而不是通过继承。例如:
class Base {
public:
    int data;
};
class Derived1 {
private:
    Base base;
public:
    // 提供访问base成员的接口
    int getData() { return base.data; }
};
class Derived2 {
private:
    Base base;
public:
    // 提供访问base成员的接口
    int getData() { return base.data; }
};
class Final {
private:
    Derived1 d1;
    Derived2 d2;
public:
    // 提供访问d1和d2中base成员的接口
    int getDataFromD1() { return d1.getData(); }
    int getDataFromD2() { return d2.getData(); }
};
  1. 优点
    • 清晰地控制了数据的访问和管理,每个派生类对包含的基类对象有明确的控制权。
    • 避免了继承体系中的一些复杂问题,如数据冗余和访问歧义。
  2. 缺点
    • 增加了代码量,需要为包含的对象提供额外的访问接口。
    • 破坏了继承的层次结构,对于一些依赖继承关系的设计模式不太友好。