文章目录
条款40:明智而审慎地使用多重继承
多重继承(Multiple Inheritance)允许一个类从多个基类继承。然而,与单一继承相比,多重继承更加复杂,可能带来以下问题和成本:
- 歧义性:当多个基类中存在相同名称的成员时,派生类的调用可能产生歧义。
- 对 virtual 继承的需求:为了避免重复继承的问题(如菱形继承),需要引入虚继承,但这会增加运行时的开销。
- 额外的开销:虚继承可能导致类的大小增加、访问速度下降,以及初始化和赋值的复杂度提高。
尽管多重继承复杂,但在某些情况下它是合理的选择,尤其是涉及以下场景:
- Interface class 的公共继承。
- 协助实现的类的私有继承。
示例 1:多重继承中的歧义
class Base1 { public: void display() const { std::cout << "Base1::display" << std::endl; } }; class Base2 { public: void display() const { std::cout << "Base2::display" << std::endl; } }; class Derived : public Base1, public Base2 { }; int main() { Derived d; // d.display(); // 错误:不明确,Base1 和 Base2 中都有 display d.Base1::display(); // 明确调用 Base1 的 display d.Base2::display(); // 明确调用 Base2 的 display return 0; }
在多重继承中,派生类可能继承多个基类中同名的成员函数。这种情况下,调用成员函数会产生歧义,必须明确指定调用哪一个基类的函数。
示例 2:避免菱形继承问题
菱形继承 问题发生在一个派生类通过不同的路径多次继承同一个基类:
class Base { public: virtual void display() const { std::cout << "Base::display" << std::endl; } }; class Derived1 : public virtual Base {}; class Derived2 : public virtual Base {}; class MostDerived : public Derived1, public Derived2 {}; int main() { MostDerived md; md.display(); // 没有歧义,只有一个 Base 子对象 return 0; }
通过使用 virtual 继承,MostDerived
类可以确保只有一个 Base
子对象存在,避免了菱形继承的歧义。
示例 3:合理使用多重继承
一个合理的多重继承设计可能是将 接口继承 和 实现继承 结合起来:
class Interface { public: virtual void process() const = 0; // 纯虚函数,提供接口 }; class Helper { protected: void assist() const { std::cout << "Helper::assist" << std::endl; } }; class Derived : public Interface, private Helper { public: virtual void process() const override { assist(); // 调用 Helper 提供的功能 std::cout << "Derived::process" << std::endl; } }; int main() { Derived d; d.process(); return 0; }
在这个例子中,Derived
类通过 public 继承 获得 Interface
的接口,通过 private 继承 使用 Helper
提供的实现。
总结
- 多重继承增加了复杂性,但在某些场景下是必要的。
- 虚继承 可以解决菱形继承问题,但也带来了额外的开销。
- 一个常见的合理用法是 公共继承接口类 和 私有继承实现类 的组合。
- 在设计中尽量避免过度使用多重继承,如果可以,用 复合 替代复杂的继承关系。