• C++多重继承与虚拟继承


      本文只是粗浅讨论一下C++中的多重继承和虚拟继承。

    多重继承中的构造函数和析构函数调用次序

      我们先来看一下简单的例子:

     1 #include <iostream>
     2 using namespace std;
     3 
     4 class A
     5 {
     6 private:
     7     char idA;
     8 
     9 public:
    10     A(){ 
    11         idA = 'A';
    12         cout << "Constructor of A is called!" << endl; 
    13     }
    14     ~A() { cout << "Destructor of A is called!" << endl; }
    15 };
    16 
    17 class B : public A
    18 {
    19 private:
    20     char idB;
    21 
    22 public:
    23     B(){
    24         idB = 'B';
    25         cout << "Constructor of B is called!" << endl;
    26     }
    27     ~B() { cout << "Destructor of B is called!" << endl; }
    28 };
    29 
    30 class C : public A
    31 {
    32 private:
    33     char idC;
    34 
    35 public:
    36     C(){
    37         idC = 'C';
    38         cout << "Constructor of C is called!" << endl;
    39     }
    40     ~C() { cout << "Destructor of C is called!" << endl; }
    41 };
    42 
    43 class D : public B, public C
    44 {
    45 private:
    46     char idD;
    47 
    48 public:
    49     D(){
    50         idD = 'D';
    51         cout << "Constructor of D is called!" << endl;
    52     }
    53     ~D() { cout << "Destructor of D is called!" << endl; }
    54 };
    55 
    56 int main()
    57 {
    58     D d;
    59     return 0;
    60 }

      上述程序的输出为:

      

      由上边结果可以看出,析构函数调用次序跟构造函数是相反的。另外,构造函数调用次序跟类D继承B、C次序(public B, public C)相关。

      可能我们也发现了,对于类D的实例d来说,它其实有两个重复的A实例。我们应该要去掉其中一个以节省空间。具体做法就是采用虚拟继承的方法:

    1 class B : public virtual A
    2 {
    3     ...
    4 };
    5 
    6 class C : public virtual A
    7 {
    8     ...
    9 };

      这是程序的输出就会变成:

      

      可见这个时候类D的实例d就只有一个类A实例。

    二义性

      请看下边程序:

     1 #include <iostream>
     2 using namespace std;
     3 
     4 class A
     5 {
     6 private:
     7     char idA;
     8 
     9 public:
    10     A(){ 
    11         idA = 'A';
    12         cout << "Constructor of A is called!" << endl; 
    13     }
    14     ~A() { cout << "Destructor of A is called!" << endl; }
    15     char getID() { return idA; }
    16 };
    17 
    18 class B  : public virtual A
    19 {
    20 private:
    21     char idB;
    22 
    23 public:
    24     B(){
    25         idB = 'B';
    26         cout << "Constructor of B is called!" << endl;
    27     }
    28     ~B() { cout << "Destructor of B is called!" << endl; }
    29     char getID() { return idB; }
    30 };
    31 
    32 class C : public virtual A
    33 {
    34 private:
    35     char idC;
    36 
    37 public:
    38     C(){
    39         idC = 'C';
    40         cout << "Constructor of C is called!" << endl;
    41     }
    42     ~C() { cout << "Destructor of C is called!" << endl; }
    43     char getID() { return idC; }
    44 };
    45 
    46 class D : public B, public C
    47 {
    48 private:
    49     char idD;
    50 
    51 public:
    52     D(){
    53         idD = 'D';
    54         cout << "Constructor of D is called!" << endl;
    55     }
    56     ~D() { cout << "Destructor of D is called!" << endl; }
    57     // char getID() { return idD; }
    58 };
    59 
    60 int main()
    61 {
    62     D d;
    63     cout << d.getID() << endl;
    64 
    65     return 0;
    66 }

      在main函数中,第63行的d.getID()会优先在类D中查找有没有getID()的定义,如果没有就会到其父类查找;而恰好其父类B、C(同级)均定义了相同的getID()(类A的getID()定义存不存在都没关系),这时d.getID()就不知道要调用B类中的getID()还是C类中的,从而导致二义性

      不过我们可以通过d.B::getID()、d.C::getID()来指明具体要调用哪一个类的getID。但我们总不会想到这样子去做,而且这样子做也比较麻烦。

    虚函数

      对于多重继承的虚函数同样存在二义性。

      先看一下程序:

     1 #include <iostream>
     2 using namespace std;
     3 
     4 class A
     5 {
     6 private:
     7     char idA;
     8 
     9 public:
    10     A(){ 
    11         idA = 'A';
    12         cout << "Constructor of A is called!" << endl; 
    13     }
    14     ~A() { cout << "Destructor of A is called!" << endl; }
    15     char getID() { return idA; }
    16 };
    17 
    18 class B : public virtual A
    19 {
    20 private:
    21     char idB;
    22 
    23 public:
    24     B(){
    25         idB = 'B';
    26         cout << "Constructor of B is called!" << endl;
    27     }
    28     ~B() { cout << "Destructor of B is called!" << endl; }
    29     char getID() { return idB; }
    30 };
    31 
    32 class C : public virtual A
    33 {
    34 private:
    35     char idC;
    36 
    37 public:
    38     C(){
    39         idC = 'C';
    40         cout << "Constructor of C is called!" << endl;
    41     }
    42     ~C() { cout << "Destructor of C is called!" << endl; }
    43     char getID() { return idC; }
    44 };
    45 
    46 class D : public B, public C
    47 {
    48 private:
    49     char idD;
    50 
    51 public:
    52     D(){
    53         idD = 'D';
    54         cout << "Constructor of D is called!" << endl;
    55     }
    56     ~D() { cout << "Destructor of D is called!" << endl; }
    57     char getID() { return idD; }
    58 };
    59 
    60 int main()
    61 {
    62     D d;
    63     A a = d;
    64     B b = d;
    65     C c = d;
    66     cout << a.getID() << endl;
    67     cout << b.getID() << endl;
    68     cout << c.getID() << endl;
    69     cout << d.getID() << endl;
    70 
    71     return 0;
    72 }

      程序输出如下:

      

      上边程序第63~65行相当于a、b、c将d进行了分割(函数是否是虚函数在这里并无关系,而且注意这里的a、b、c、d都不是指针),分割出属于自己的部分,所以调用getID()的时候能正确反映具体的类。

      

      我们再来看一个程序:

     1 #include <iostream>
     2 using namespace std;
     3 
     4 class A
     5 {
     6 private:
     7     char idA;
     8 
     9 public:
    10     A(){ 
    11         idA = 'A';
    12         cout << "Constructor of A is called!" << endl; 
    13     }
    14     ~A() { cout << "Destructor of A is called!" << endl; }
    15     virtual char getID() { return idA; }
    16 };
    17 
    18 class B : public virtual A
    19 {
    20 private:
    21     char idB;
    22 
    23 public:
    24     B(){
    25         idB = 'B';
    26         cout << "Constructor of B is called!" << endl;
    27     }
    28     ~B() { cout << "Destructor of B is called!" << endl; }
    29     virtual char getID() { return idB; }
    30 };
    31 
    32 class C : public virtual A
    33 {
    34 private:
    35     char idC;
    36 
    37 public:
    38     C(){
    39         idC = 'C';
    40         cout << "Constructor of C is called!" << endl;
    41     }
    42     ~C() { cout << "Destructor of C is called!" << endl; }
    43     virtual char getID() { return idC; }
    44 };
    45 
    46 class D : public B, public C
    47 {
    48 private:
    49     char idD;
    50 
    51 public:
    52     D(){
    53         idD = 'D';
    54         cout << "Constructor of D is called!" << endl;
    55     }
    56     ~D() { cout << "Destructor of D is called!" << endl; }
    57     virtual char getID() { return idD; }
    58 };
    59 
    60 int main()
    61 {
    62     D *d = new D();
    63     A *a = d;
    64     B *b = d;
    65     C *c = d;
    66     cout << a->getID() << endl;
    67     cout << b->getID() << endl;
    68     cout << c->getID() << endl;
    69     cout << d->getID() << endl;
    70 
    71     delete d;
    72     return 0;
    73 }

      程序的输出如下:

      

      从输出结果可以看出,类D的getID覆盖其所有父类的getID。需要注意的是,当我们在类D的一个父类,如A中不设定getID为虚函数,则“A *a = d”的效果仍然跟分割d指向的内存的效果一样。

  • 相关阅读:
    vue 组件的简单使用01
    vue 绑定 class 和 内联样式(style)
    input select 值得绑定与获取
    computed 计算属性
    v-for 循环 绑定对象 和数组
    过滤器 filter
    v-model 双向数据绑定以及修饰符
    v-on 绑定单个或多个事件
    v-bin:href 绑定链接
    .net core自动发送后台请求写法
  • 原文地址:https://www.cnblogs.com/xiehongfeng100/p/4708567.html
Copyright © 2020-2023  润新知