一、多继承 " 弊端 "



1、多继承被禁用的场景


禁止使用多继承的场景 : 在 C++ 语言 环境 中 , 多继承 若干完整的 有成员函数 和 成员变量 的 类 , 是不推荐的做法 , 实际开发中 , 绝对禁止 使用 上述类型的 多继承 ;


2、多继承弊端


多继承会带来一系列的问题 , 诸如 :

  • 钻石问题 - 菱形继承结构 / 二义性错误 : 当一个类继承自多个类时 , 如果这些类有共同的基类 , 那么会出现菱形继承结构 , 也称为钻石问题 ; 该场景下 , C++ 编译器 无法确定应该使用哪个基类的成员 , 产生 二义性 ;
  • 成员变量名相同 - 二义性错误 : 子类继承多个父类 , 父类之间没有相同的父类 , 但是 父类中 有相同名称的成员变量 , 此时也会产生二义性问题 , 需要使用域作用符访问父类中相同名称的成员 ;
  • 破坏封装性 : 多继承 可能会破坏类的封装性 , 由于一个类需要实现多个基类的接口 , 因此它必须公开更多的实现细节 , 这可能会降低代码的可维护性和可重用性 ;
  • 复杂的构造和析构过程 : 多继承 的 子类 的 构造和析构过程 可能会变得非常复杂 , 当一个子类对象被创建时 , 需要调用所有基类的构造函数 ; 当一个对象被销毁时 , 需要调用所有基类的析构函数 ; 构造 和 析构 期间 , 可能会导致资源泄漏或程序崩溃等问题 ;
  • 应用二进制接口 ABI 兼容性问题 : 多继承可能会导致 ABI ( 应用二进制接口 Application Binary Interface ) 兼容性问题 , 不同的 编译器和操作系统可能会有不同的ABI规范 , 这可能会导致在不同的平台上使用不同的编译器编译的代码时出现问题 ;

使用 虚继承 解决二义性问题 ;

在 菱形继承结构 中 , 虚继承可以使 重复继承 的 父类 , 只继承 依次 ;


多继承的二义性 参考 【C++】继承 ⑫ ( 继承的二义性 | virtual 虚继承 ) 博客 ;

【C++】多态 ⑫ ( 多继承 “ 弊端 “ | 多继承被禁用的场景 | 菱形继承结构的二义性 | 使用虚继承解决菱形继承结构的二义性 )-LMLPHP





二、代码示例 - 多继承弊端




1、错误示例 - 菱形继承结构的二义性


在下面的 菱形继承结构 中 , D 继承 B 和 C 类 ,

但是 B 和 C 有共同的 父类 A ,

假如 访问 D 对象 中 继承自 A 类的成员 , 会产生二义性 ;


代码示例 :

#include "iostream"
using namespace std;

class A {
public:
	int x;
};

// 子类 B 继承了父类 A 的 x 成员
class B : public A {
public:
	int y;
};

// 子类 C 继承了父类 A 的 x 成员
class C : public A {
public:
	int z;
};

// D 多继承 B 和 C 
// 分别从 B 和 C 各自继承一个来自 A 的成员 x
class D : public B, public C {
public:
	int k;
};

int main() {

	// 定义 D 类对象 d
	D d;

	// 访问 继承自 A 类的 x 成员出现二义性
	// 报错 error C2385: 对“x”的访问不明确
	d.x = 40;
	
	// 控制台暂停 , 按任意键继续向后执行
	system("pause");

	return 0;
}

执行结果 :
1>------ 已启动生成: 项目: HelloWorld, 配置: Debug Win32 ------
1>Test.cpp
1>Y:\002_WorkSpace\002_VS\HelloWorld\HelloWorld\Test.cpp(35,6): error C2385: 对“x”的访问不明确
1>Y:\002_WorkSpace\002_VS\HelloWorld\HelloWorld\Test.cpp(35,6): message : 可能是“x”(位于基“A”中)
1>Y:\002_WorkSpace\002_VS\HelloWorld\HelloWorld\Test.cpp(35,6): message : 也可能是“x”(位于基“A”中)
1>已完成生成项目“HelloWorld.vcxproj”的操作 - 失败。
========== 生成: 成功 0 个,失败 1 个,最新 0 个,跳过 0 个 ==========
【C++】多态 ⑫ ( 多继承 “ 弊端 “ | 多继承被禁用的场景 | 菱形继承结构的二义性 | 使用虚继承解决菱形继承结构的二义性 )-LMLPHP


2、代码示例 - 使用虚继承解决菱形继承结构的二义性


在下面的 菱形继承结构 中 , D 继承 B 和 C 类 ,

但是 B 和 C 有共同的 父类 A , 这里 B 和 C 使用 虚继承 , 继承 A 类 ;

假如 访问 D 对象 中 继承自 A 类的成员 , 不会产生二义性 ;


代码示例 :

#include "iostream"
using namespace std;

class A {
public:
	int x;
};

// 子类 B 继承了父类 A 的 x 成员
class B : virtual public A {
public:
	int y;
};

// 子类 C 继承了父类 A 的 x 成员
class C : virtual public A {
public:
	int z;
};

// D 多继承 B 和 C 
// 分别从 B 和 C 各自继承一个来自 A 的成员 x
class D : public B, public C {
public:
	int k;
};

int main() {

	// 定义 D 类对象 d
	D d;

	// 访问 继承自 A 类的 x 成员不会出现二义性
	d.x = 40;
	
	// 控制台暂停 , 按任意键继续向后执行
	system("pause");

	return 0;
}

执行结果 :
【C++】多态 ⑫ ( 多继承 “ 弊端 “ | 多继承被禁用的场景 | 菱形继承结构的二义性 | 使用虚继承解决菱形继承结构的二义性 )-LMLPHP

11-05 05:58