c++ - 如何正确实现多重继承?

标签 c++ inheritance c++11 multiple-inheritance

<分区>

我花了很多时间搜索关于这个主题的信息,但我只能找到零碎的信息,被大量不要使用多重继承的警告所笼罩。

我对多重继承有多糟糕不感兴趣。我对有效用例也不感兴趣。我得到的信息是,您应该尽可能避免使用它,并且几乎总是有更好的选择。

但我想知道的是,在一个彻底的层面上,您何时决定使用多重继承,您如何正确地使用它?

我希望看到更彻底解释的子主题是:

  • 多态性的精确机制
    • 混合虚基类和纯虚基类
    • 重复函数
  • 内存管理
  • 在多个层面解决菱形继承(钻石问题)
  • 混合公共(public)和私有(private)继承
  • 混合虚拟和非虚拟继承

并且,如果适用的话:

  • C++ 和 C++11 的区别

最佳答案

采用以下层次结构:

  • 基类A
  • BCE 继承自 A
  • D 继承自 BC
  • F 继承自 DE

用代码来表达:

class A { public: int a; }
class B : public A { }
class C : public A { }
class D : public B, public C { }
class E : public A { }
class F : public D, public E { }

或者,图表:

       A     A   A
       |     |   |
       |     |   |
       B     C   E
        \   /   /
         \ /   /
          D   /
           \ /
            F

在这种结构中,每个 B、C 和 E 都拥有自己的 A 拷贝。之后,D 拥有 B 和 C 的拷贝,F 拥有 D 和 E 的拷贝。

这会导致一个问题:

D d;
d.a = 10; // ERROR! B::a or C::a?

对于这种情况,您可以使用虚拟继承,创建一个“钻石”:

          A      A
         / \     |
        /   \    |
       B     C   E
        \   /   /
         \ /   /
          D   /
           \ /
            F

或者,在代码中:

class A { public: int a; }
class B : public virtual A { }
class C : public virtual A { }
class D : public B, public C { }
class E : public A { }
class F : public D, public E { }

现在你解决了前面的问题,因为B::aC::a共享相同的内存,但同样的问题仍然存在,在另一个层面:

F f;
f.a = 10; // ERROR: D::a or E::a ?

这部分我不确定 Confirmed :您也可以使用从 A 继承 E 的 virtual 来解决这里的问题。但我将保留原样,以便回答另一点:混合虚拟和非虚拟继承。

但考虑到您希望 F 中的 E::a 具有来自相同 D::a 的不同值 F。为此,您必须对 F 进行类型转换:

F *f = new F;
(static_cast<D*>(f))->a = 10;
(static_cast<E*>(f))->a = 20;

现在您的 F* f 包含 A::a 的两个不同值。

关于内存管理

从上面的例子中获取这些类:

class A { public: int a; }
class B : public virtual A { }
class C : public virtual A { }
class D : public B, public C { }
class E : public A { }
class F : public D, public E { }

可以画出下面的内存图:

对于 A 类:

+---------+
|    A    |
+---------+

对于 B、C 和 E 类:

+---------+
|    B    |
+---------+
     |
     V
+---------+
|    A    |
+---------+

这意味着对于您创建的每个 B、C 和 E 实例,您都会创建另一个 A 实例。

对于 D 类,事情有点复杂:

+---------------------------------------+
|                   D                   |
+---------------------------------------+
       |                         |
       V                         V
+--------------+         +--------------+
|      B       |         |       C      |
+--------------+         +--------------|
       |                         |
       V                         V
+---------------------------------------+
|                   A                   |
+---------------------------------------+

这意味着当您创建一个 D 时,您有一个 B 实例和一个 C 实例。但是不是为每个 B 和 C 创建一个新的 A 实例,而是为两者创建一个实例。

对于 F:

+-------------------------------------------------------+
|                           F                           |
+-------------------------------------------------------+
                    |                              |
                    V                              V
+---------------------------------------+     +---------+
|                   D                   |     |    E    |
+---------------------------------------+     +---------+
       |                         |                 |
       V                         V                 |
+--------------+         +--------------+          |   
|      B       |         |       C      |          |
+--------------+         +--------------+          |
       |                         |                 |
       V                         V                 V
+---------------------------------------+     +---------+
|                   A                   |     |    A    |
+---------------------------------------+     +---------+

这意味着当你创建一个 F 时,你有:一个 D 的实例和一个 E 的实例。由于 E 实际上并没有继承自 A,因此在创建 E 时会创建一个新的 A 实例。

关于虚方法和纯虚方法

参加这些类(class):

class A { virtual void f() = 0; }
class B : public A { virtual void f(int value) { std::cout << "bar" << value; } }
class C : public B { virtual void f() { std::cout << "foo"; f(42); } }

A被称为abstract(有的也称为interface),因为有纯虚函数。

B 也是 abstract,因为它继承自 A 并且不会覆盖 A::f(void) 方法,它是纯虚拟的,甚至定义了自己的方法 (B::f(int))

CB实现,因为它确实定义了转换 B 所需的所有函数进入一个“完整”的类——它覆盖了 A::f(void)

这个答案并不完整,但它给出了一个大概的想法。

关于c++ - 如何正确实现多重继承?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23836737/

相关文章:

php - 在 PHP 中访问父属性和重写的方法

c++ - 区分具有相同类型和比率的 std::chrono 持续时间?

c++ - 检查类型是否来自特定命名空间

c++ - Google.Com 上的 Http GET 请求返回 "Error 302 page has been moved"

c++ - 无法找到请求的 Boost 库 Windows Cmake

javascript - 我可以在不使用 new 关键字的情况下构造 JavaScript 对象吗?

c++ - 另一个包含的对象如何改变容器对象的私有(private)数据成员?

c++ - 如何在没有内存泄漏的情况下同时(线程安全)填充 c++11 std::map<std::string,std::bitset<N>*>?

c++ - 如何在Visual Studio 2015中添加库、头文件

c++ - 如何在不增加 sizeof 的情况下将 bool 添加到结构中(如果结构中有填充)?