• EffectiveC++-条款40:明智而审慎地使用多重继承


    一. 内容

    1. 一旦涉及多重继承,C++社群便分为两个基本阵营。其中之一认为:如果单一继承是好的,多重继承一定更好。另一阵营认为:单一继承是好的,但多重继承不值得拥有。本条款主要目的是让大家了解这两个多重继承的基本观点。

    2. 最先要认清的一件事是:使用多重继承会导致 class 可能从一个以上的 base class 继承相同名称(如函数,变量,typedefs等),那会导致较多的歧义。C++在解析重载函数调用时,会先查找是否具有对此调用匹配的函数,找出最佳匹配后再检验其可取性。这意味着即使在 base class 声明为 private 的函数也会被纳入匹配的范围,导致可能的二义性问题

      为了解决这个歧义,你必须明确指出你要调用哪个 base class 的函数。例如 xxx::yyy()

    3. 多重继承中,如果有一个 derived class 可由 base class 经过多个路径到达,你就必须面对这样一个问题,是否打算让 base class 的成员变量经由每一条路径被复制?比如下面例子:

      在这里插入图片描述
      如果 File 成员每一条到 IOFile 的路径都被复制,意味着 IOFile 将持有两份一模一样的 File 成员。但简单的逻辑告诉我们,这种重复是没有必要的。

      C++ 的默认做法是执行复制。如果你不想要这种复制的默认行为,可以使用 virtual 关键字,使 File 成为 virtual base class。

      在这里插入图片描述

      这样可以保证 IOFile 只会持有一份来自 File 的成员。

    4. 从合理行为的观点来看, public 继承应该总是使用 virtual。这样总是可以保证 derived class 总是不会重复获得成员。但是合理并不是唯一观点,virtual 继承需要付出代价,使用 virtual 继承的那些 class 产生的对象往往比 non-virtual 继承的对象体积大,访问速度慢。种种细节因编译器不同而异,但可以确定一点,你得为此付出代价。

    5. virtual 继承的成本还包括其他方面。比如 virtual class 的初始化责任是由继承体系中最底层的(most derived)class 负责,这暗示 class 若派生自 virtual classes 而需要初始化,必须认识其 virtual class,不论继承体系有多少,且必须负责其所有 virtual class 的初始化责任。

    6. 我对 virtual base classes 的看法很简单:第一,非必要不要使用 virtual classes,平常请使用 non-virtual 继承。第二,如果你必须要使用 virtual base class,请避免在类中放置数据。这样就不必担心 virtual classes 的初始化责任。类似 interfaces 的结构很适合于此。

    7. 那么对于接口和实现分开的设计,多重继承显然就可以发挥其作用。举个 Person class 的简单例子:

      class IPerson {
      public:
          virtual ~IPerson() = default;
          virtual std::string GetName() const =0;
      };
      
      class IPersonImpl {
      public:
          virtual ~IPersonImpl() = default;
          explicit IPersonImpl(std::string InName): Name(std::move(InName)) {}
      
          virtual std::string GetNameImpl() const {
              return Name;
          }
      
      private:
          std::string Name;
      };
      
      class APerson : public IPerson, private IPersonImpl {
      public:
          explicit APerson(const std::string& InName)
              : IPersonImpl(InName) {}
      
          std::string GetName() const override {
              return IPersonImpl::GetNameImpl();
          }
      };
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28

      使用 public 继承其接口,private 继承其实现,似乎是再完美不过的一件事。这个例子告诉我们,多重继承也有它的合理用途。

    8. 最后,我想说,多重继承只是面向对象工具箱的一个工具而已。和单一继承相比,它通常比较复杂,使用上也难以理解。所以如果你有个单一继承的方案,也有一个多重继承的方案,那么单一继承的设计方案一定比较受欢迎。然而有时候多重继承确实是完成任务之最简洁,最易维护,最合理的方案,那就别害怕使用它们,只要确定,你的确是在明智而审慎的情况下使用它。

    二. 总结

    1. 多重继承比单一继承复杂。它可能导致新的歧义性,以及对 virtual 继承的需要。
    2. virtual 继承会增加大小,速度,初始化(赋值)复杂度等等成本。如果 virtual base classes 不带任何数据,将是最具有实用价值的情况。
    3. 多重继承的确有正当用途。其中一个情节涉及 public 继承某个 Interface class 和 private 继承某个协助实现的 class 的两相组合。
  • 相关阅读:
    用java写点题-lc104. 二叉树的最大深度
    [附源码]Python计算机毕业设计Django校园生活服务平台
    矩阵分析与应用-14-行列式
    第一行代码Android 第九章9.4-9.5(解析JSON格式,网络编程最佳实践:发送HTTP请求的代码)
    ORACLE-SQL 关于树结构的查询
    Linux驱动开发 问题随笔
    设计模式之备忘录模式
    c#快速入门~在java基础上,知道C#和JAVA 的不同即可
    SpringBoot+Vue项目线上买菜系统
    python正则表达式
  • 原文地址:https://blog.csdn.net/m0_51819222/article/details/125889589