当前位置:   article > 正文

C++菱形继承模型刨析_菱形继承的基类析构函数调用几次

菱形继承的基类析构函数调用几次
继承概念:
继承机制是新的类从已有类那里得到已有的特性。亦或从已有类产生新类的过程就是类的派生。原有的类称为基类或父类,产生的新类称为派生类或子类。它是使代码可以复用的最重要的手段。


继承关系和访问限定符:


 
<单继承>
一个类只有一个直接父类时,称这个继承关系为单继承

  1. #include<iostream>
  2. class Base
  3. {
  4. public:
  5. Base(int pub = 0, int pro = 1, int pri = 2)
  6. :_pub(pub),
  7. _pro(pro),
  8. _pri(pri)
  9. {
  10. cout << "B()" << endl;
  11. }
  12. ~Base()
  13. {
  14. cout << "~B()" << endl;
  15. }
  16. void ShowBase()
  17. {
  18. cout << "_pri = " << _pri << endl;
  19. cout << "_pro = " << _pro << endl;
  20. cout << "_pub = " << _pub << endl;
  21. }
  22. public:
  23. int _pub;
  24. protected:
  25. int _pro;
  26. private:
  27. int _pri;
  28. };
  29. class Derived :public Base
  30. {
  31. public:
  32. Derived(int Dpub = 3,int Dpro=4,int Dpri =5)
  33. :_Dpub(Dpub),
  34. _Dpro(Dpro),
  35. _Dpri(Dpri)
  36. {
  37. cout << "Derived()" << endl;
  38. }
  39. ~Derived()
  40. {
  41. cout << "~Derived()" << endl;
  42. }
  43. void ShowDerived()
  44. {
  45. cout << "_pro=" << _pro << endl;
  46. cout << "_Dpri = " << _Dpri << endl;
  47. cout << "_Dpro = " << _Dpro << endl;
  48. cout << "_Dpub = " << _Dpub << endl;
  49. }
  50. public:
  51. int _Dpub;
  52. protected:
  53. int _Dpro;
  54. private:
  55. int _Dpri;
  56. };
  57. void test0()
  58. {
  59. Derived d1;
  60. d1.ShowBase();
  61. d1.ShowDerived();
  62. d1._pub = 10;
  63. d1._Dpub = 11;
  64. d1.ShowBase();
  65. d1.ShowDerived();
  66. }
  67. int main()
  68. {
  69. test0();
  70. return 0;
  71. }
 
一下是单继承模式下Derived类继承Base类的之后的模型。
基类的成员变量在上面,派生类成员变量在基类下面。
运行结果:

总结:
1.基类的private成员在派生类是不能被访问的(不可见),如果基类成员不想在类外直接被访问,但需要在派生中能访问,就应该定义为protected。就可以看出保护成员限定符是因为继承才出现的。
2.public继承是一个接口继承,保持is—a原则,每个父类可用的成员对子类也可用,因为每个子类对象也是一个父类对象。
3.protected/private继承是一个实现继承,基类的部分成员并非完全成为子类接口的一部分,是has-a的关系
原则。
4.不管那种继承方式,在派生类内部都可以访问基类的公有成员和保护成员,基类的私有成员不管在哪种继承方式下在派生类都是不可见的。
5.使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public。(建议最好写出继承方式)

继承关系中构造函数的调用顺序:
编译器调用派生类构造函数---》转调基类构造函数---》再执行派生类构造函数体。
1.基类构造函数调用顺序按照继承列表中的顺序调用。
2.构造派生类 类成员时,按照在派生类中声明的次序调用。
【说明】
1.基类没有缺省构造函数,派生类必须要在初始列表中显示调用基类的构造函数。
2.基类如果没有定义构造函数,派生类也可以选择不定义,全部使用缺省构造函数。
3.如果基类没有缺省构造函数,派生类必须要定义一个构造函数,在初始化列表中显示调用基类构造函数。
继承关系中析构函数的调用过程:
派生类析构函数---》派生类包含成员对象析构函数(调用顺序和成员对象在基类中声明的顺序相反)--》基类析构函数(调用顺序和基类在派生列表中的声明顺序相反)

继承体系的作用域:
#include<iostream> using namespace std;
class Base { public:  void display()  {   cout << "父类函数" << endl;  }
private:
}; class Deiverd:public Base { public:  void display()  {   cout << "子类函数" << endl;  } };
int main() {  Deiverd d1;  d1.display();  return 0; }
运行结果:
 
1.在继承体系中基类和派生类是两个不同的作用域。
2.子类和父类中有同名成员,子类成员将屏蔽对父类成员的直接访问。(在子类成员函数中,可以使用基类::基类成员 访问)构成同名隐藏---重定义。(在实际应用中,除非必要否则应尽量避免定义同名成员)
(1)同名隐藏和函数重载:
继承与转换--赋值兼容规则--(必须是public):
1.子类对象可以赋值给父类对象(切割/切片)
2.父类对象不能赋值给子类对象
3.父类指针或引用可以指向子类对象
4.子类的指针或引用不能指向父类对象(可以通过强制类型转换完成)
友元和静态成员与继承的关系:
1.友元关系不能继承,也就是基类友元不能访问子类私有和保护(友元函数并不是成员函数,所以并不能继承下来)
2.基类定义了static成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子类,都只有一个static成员实例(静态成员是属于类的,是所有对象共享一个)
单继承&&多继承&&菱形继承
单继承已在上面说明,我们看一下多继承的模型,一个类有两个及两个以上的直接父类时,称这个继承为多继承。
#include<iostream> using namespace std;
class Base { public:  Base(int pub = 0, int pri = 1) :_pub(pub), _pri(pri)  {   cout << "Base()" << endl;  }  int _pub; private:  int _pri;
}; class Base0 { public:  Base0(int pub0 = 2, int pri0 = 3) :_pub0(pub0), _pri0(pri0)  {   cout << "Base0()" << endl;  }  int _pub0; private:  int _pri0; }; class Deiverd:public Base,public Base0 { public:  Deiverd(int Dpub = 4, int Dpri = 5) :_Dpub(Dpub), _Dpri(Dpri)  {   cout << "Deiverd()" << endl;  }  int _Dpub; private:  int _Dpri; }; void test0() {  Deiverd d1; } int main() {  test0();  return 0; }
继承模型如下:
菱形继承:
#include<iostream> using namespace std;
class Base { public:  Base(int pub = 0, int pri = 1) :_pub(pub), _pri(pri)  {   cout << "Base()" << endl;  }  int _pub; private:  int _pri;
}; class Deiverd0:public Base { public:  Deiverd0(int Dpub0 = 2, int Dpri0 = 3) :_Dpub0(Dpub0), _Dpri0(Dpri0)  {   cout << "Deiverd0()" << endl;  }  int _Dpub0; private:  int _Dpri0; }; class Deiverd:public Base { public:  Deiverd(int Dpub = 4, int Dpri = 5) :_Dpub(Dpub), _Dpri(Dpri)  {   cout << "Deiverd()" << endl;  }  int _Dpub; private:  int _Dpri; }; class Final:public Deiverd0,public Deiverd { public:  Final(int fpub = 7, int fpri = 8) :_fpub(fpub), _fpri(fpri)  {   cout << "Final()" << endl;  }  int _fpub; private:  int _fpri; }; void test0() {  Final f1;  //f1._pub = 5;  f1.Deiverd::_pub = 5; } int main() {  test0();  return 0; }
菱形继承模型图分析:
根据内存分布情况画出抽象图:
通过上图可以看出基类Base在Final创建的对象里面一共保存了两份。                                 当你尝试用此语句:f1._pub=5;时编译器会报错
                                                       因为编译器并不知道你要访问是那一个_pub,是Deiverd继承来的?亦或是Deiverd0继承来的,所以编译器只能给你报出Final::_pub不明确;但是你可以通过语句:f1.Deiverd::_pub = 5;或者f1.Deiverd0::_pub = 5;来访问_pub。
菱形继承总结:
1.菱形继承存在明显的二义性。
2.菱形继承存在数据的冗余,造成空间的大量浪费。
3.在设计时尽量不要用菱形继承。

以上就是我关于继承部分的理解,关于菱形虚拟继承将在下一部分进行刨析,如果有那里不对的地方,还望不吝赐教,在下方留言或者私信我。


声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/神奇cpp/article/detail/977485
推荐阅读
相关标签
  

闽ICP备14008679号