C++在单继承、多继承
更新时间:2024-03-13 17:56:01 阅读量: 综合文库 文档下载
一、本文目的与说明
1. 本文目的:理清在各种继承时,构造函数、复制构造函数、赋值操作符、析构函数的执行顺序和执行内容。
2. 说明:虽然复制构造函数属于构造函数的一种,有共同的地方,但是也具有一定的特殊性,所以在总结它的性质时将它单独列出来了。
3. 单继承、多继承、虚继承,既然都属于继承,那么虽然有一定的区别,但还是相同点比较多。如果放在一块讲,但为了将内容制作成递进的,就分开了,对相同点进行重复,(大量的复制粘贴哈),但在不同点进行了标注。 注意:三块内容是逐步递进的
如果你懂虚函数,那么单继承和多继承那块你就可以不看;
如果你懂多继承,那单继承你就不要看了,至于虚继承就等你懂虚继承再回来看吧;
如果你只懂单继承,那你就只看单继承就好。
二、基本知识
1. 对于一个空类,例如;
[cpp] view plaincopyprint?
1. class EmptyClass{};
虽然你没有声明任何函数,但是编译器会自动为你提供上面这四个方法。
[cpp] view plaincopyprint?
1. class EmptyClass { 2. public:
3. EmptyClass(); // 默认构造函数 4. EmptyClass(const EmptyClass &rhs); // 复制构造函数 5. ~EmptyClass(); // 析构函数
6. EmptyClass& operator=(const EmptyClass &rhs); // 赋值运算符 7. }
对于这四个方法的任何一个,你的类如果没有声明,那么编译器就会自动为你对应的提供一个默认的。(在《C++ primer》中,这个编译器自动提供的版本叫做“合成的***”,例如合成的复制构造函数)当然如果你显式声明了,编译器就不会再提供相应的方法。 2. 合成的默认构造函数执行内容:如果有父类,就先调用父类的默认构造函数。 2. 合成的复制构造函数执行内容:使用参数中的对象,构造出一个新的对象。
3. 合成的赋值操作符执行内容:使用参数中的对象,使用参数对象的非static成员依次对目标对象的成员赋值。注意:在赋值操作符执行之前,目标对象已经存在。
4. 在继承体系中,要将基类(或称为父类)的析构函数,声明为virtual方法(即虚函数)。
5. 子类中包含父类的成员。即子类有两个部分组成,父类部分和子类自己定义的部分。 6. 如果在子类中显式调用父类的构造函数,只能在构造函数的初始化列表中调用,并且只能调用其直接父类的。
7. 在多重继承时,按照基类继承列表中声明的顺序初始化父类。
8. 在虚继承中,虚基类的初始化 早于 非虚基类,并且子类来初始化虚基类(注意:虚基类不一定是子类的直接父类)。
三、单继承
核心:在构造子类之前一定要执行父类的一个构造函数。 1.构造函数(不包括复制构造函数)。 顺序:①直接父类;②自己
注意:若直接父类还有父类,那么“直接父类的父类”会在“直接父类” 之前构造。可以理解为这是一个递归的过程,知道出现一个没有父类的类才停止。
2.1 如果没有显式定义构造函数,则“合成的默认构造函数”会自动调用直接父类的“默认构造函数”,然后调用编译器为自己自动生成的“合成的默认构造函数”。 2.2 如果显式定义了自己的构造函数
2.2.1 如果没有显式调用直接父类的任意一个构造函数,那么和“合成的默认构造函数”一样,会先自动调用直接父类的默认构造函数,然后调用自己的构造函数。
2.2.2 如果显式调用了直接父类的任意一个构造函数,那么会先调用直接父类相应的构造函数,然后调用自己的构造函数。 2. 复制构造函数
顺序:①直接父类;②自己
注意:和构造函数一样,若直接父类还有父类,那么“直接父类的父类”会在“直接父类” 之前构造。可以理解为这是一个递归的过程,知道出现一个没有父类的类才停止。
2.1 如果 没有显式定义复制构造函数,则“合成的复制构造函数”会自动调用直接父类的“复制构造函数”,然后调用编译器为自己自动生成的“合成的复制构造函数”(注意:不是默认构造函数)
2.2 如果显式定义了自己的复制构造函数(和构造函数类似)
2.2.1 如果没有显式调用父类的任意一个构造函数,那么会先调用直接父类的默认构造函数(注意:不是复制构造函数)。
2.2.2 如果显式调用了直接父类的任意一个构造函数,那么会先调用直接父类相应的构造函数。 3.赋值操作符重载
3.1 如果没有显式定义,会自动调用直接父类的赋值操作符。(注意:不是默认构造函数)
3.2 如果显式定义了,就只执行自己定义的版本,不再自动调用直接父类的赋值操作符,只执行自己的赋值操作符。
注意:如有需要对父类子部分进行赋值,应该在自己编写的代码中,显式调用父类的赋值操作符。 4. 析构函数
与构造函数顺序相反。
四、多继承
和单继承的差别就是:需要考虑到多个直接父类。其它的都相同 1.构造函数(不包括复制构造函数)。
顺序:①所有直接父类;(按照基类继承列表中声明的顺序)②自己
注意:若直接父类还有父类,那么“直接父类的父类”会在“直接父类” 之前构造。可以理解为这是一个递归的过程,知道出现一个没有父类的类才停止。
2.1 如果 没有显式定义构造函数,则“合成的默认构造函数”会自动依次调用所有直接父类的“默认构造函数”,然后调用编译器为自己自动生成的“合成的默认构造函数”。 2.2 如果显式定义了自己的构造函数
2.2.1 如果没有显式调用父类的任意一个构造函数,那么和“合成的默认构造函数”一样,会自动依次调用所有直接父类的默认构造函数,然后调用自己的构造函数。
2.2.2 如果显式调用了父类的任意一个构造函数,那么按照基类列表的顺序,对于每一个父类依次判断:若显式调用了构造函数,那么会调用该父类相应的构造函数;如果没有显式调用,就调用默认构造函数。最后调用自己的构造函数。 2. 复制构造函数
顺序:①所有直接父类;(按照基类继承列表中声明的顺序)②自己
注意:和构造函数一样,若直接父类还有父类,那么“直接父类的父类”会在“直接父类” 之前构造。可以理解为这是一个递归的过程,知道出现一个没有父类的类才停止。 2.1 如果 没有显式定义复制构造函数,则“合成的复制构造函数”会自动依次调用所有直接父类的“复制构造函数”,然后调用编译器为自己自动生成的“合成的复制构造函数(”注意:不是默认构造函数)
2.2 如果显式定义了自己的复制构造函数(和构造函数类似)
2.2.1 如果没有显式调用父类的任意一个构造函数,那么会先自动依次调用直接父类的默认构造函数(注意:不是复制构造函数)。
2.2.2 如果显式调用了直接父类的任意一个构造函数,那么按照基类列表的顺序,对于每一个父类依次判断:若显式调用了构造函数,那么会调用该父类相应的构造函数;如果没有显式调用,就调用默认构造函数。最后调用自己的复制构造函数。 3.赋值操作符重载
3.1 如果没有显式定义,会自动依次调用直接父类的赋值操作符。(注意:不是默认构造函数)
3.2 如果显式定义了,就只执行自己定义的版本,不再自动调用直接父类的赋值操作符,只执行自己的赋值操作符。
注意:如有需要对父类子部分进行赋值,应该在自己编写的代码中,显式调用所有直接父类的赋值操作符。 4. 析构函数
与构造函数顺序相反。
五、虚继承
和多继承的差别就是:要考虑到虚基类,其它的都相同。(虚基类的初始化要早于非虚基类,并且只能由子类对其进行初始化) 1.构造函数(不包括复制构造函数)。
顺序:①所有虚基类(按照基类继承列表中声明的顺序进行查找);②所有直接父类;(按照基类继承列表中声明的顺序)③自己
注意:若虚基类或者直接父类还有父类,那么“直接父类的父类”会在“直接父类” 之前构造,“虚基类的父类”也会在“虚基类”之前构造。可以理解为这是一个递归的过程,知道出现一个没有父类的类才停止。
2.1 如果 没有显式定义构造函数,则“合成的默认构造函数”会先依次调用所有虚基类的默认构造函数,然后再自动依次调用所有直接父类的“默认构造函数”,最后调用编译器为自己自动生成的“合成的默认构造函数”。
2.2 如果显式定义了自己的构造函数 2.2.1 如果没有显式调用父类的任意一个构造函数,那么和“合成的默认构造函数”一样,会先依次调用所有虚基类的默认构造函数,然后再自动依次调用所有直接父类的默认构造函数,最后调用自己的构造函数。
2.2.2 如果显式调用了父类的任意一个构造函数,那么按照基类列表的顺序,先初始化所有虚基类,再初始化所有直接父类。对于每一个父类依次判断:若显式调用了构造函数,那么会调用该父类相应的构造函数;如果没有显式调用,就调用默认构造函数。最后调用自己的构造函数。 2. 复制构造函数
顺序:①所有虚基类(按照基类继承列表中声明的顺序进行查找);②所有直接父类;(按照基类继承列表中声明的顺序)③自己
注意:和构造函数一样,若虚基类或者直接父类还有父类,那么“直接父类的父类”会在“直接父类” 之前构造,“虚基类的父类”也会在“虚基类”之前构造。可以理解为这是一个递归的过程,知道出现一个没有父类的类才停止。
2.1 如果 没有显式定义复制构造函数,则“合成的复制构造函数”会自动依次调用所有直接父类的“复制构造函数”,然后调用编译器为自己自动生成的“合成的复制构造函数(”注意:不是默认构造函数)
2.2 如果显式定义了自己的复制构造函数(和构造函数类似)
2.2.1 如果没有显式调用父类的任意一个构造函数,那么会先依次调用所有虚基类的默认构造函数,然后再依次调用所有直接父类的默认构造函数(注意:不是复制构造函数)。 2.2.2 如果显式调用了直接父类的任意一个构造函数,那么按照基类列表的顺序,先初始化所有虚基类,再初始化所有直接父类。对于每一个父类依次判断:若显式调用了构造函数,那么会调用该父类相应的构造函数;如果没有显式调用,就调用默认构造函数。 3.赋值操作符重载
3.1 如果没有显式定义,会自动依次调用所有虚基类和所有直接父类的赋值操作符。(注意:不是默认构造函数)
3.2 如果显式定义了,就只执行自己定义的版本,不再自动调用直接父类的赋值操作符,只执行自己的赋值操作符。
注意:如有需要对父类子部分进行赋值,应该在自己编写的代码中,显式调用所有虚基类和所有直接父类的赋值操作符。 4. 析构函数
与构造函数顺序相反。
六、总结:
1. 整体顺序:虚基类 --> 直接父类 -->自己
2. 在任何显式定义的构造函数中,如果没有显式调用父类的构造函数,那么就会调用父类的默认构造函数。
3. 合成的复制构造函数、合成的赋值操作符,(当没有显式定义时,编译器自动提供),会自动调用的是虚基类和直接父类的复制构造函数和赋值操作符,而不是默认构造函数; 4. 自己显式定义的复制构造函数,除非在初始化列表中显示调用,否则只会调用虚基类和父类的默认构造函数。
5. 自己显式定义的赋值操作符,除非显式调用,否则只执行自己的代码。 6. 析构函数的执行顺序与构造函数相反。
七、例子程序
话说只有自己写一个程序,然后研究运行结果,才会掌握的更好。所以下面就是个例子程序了。可以根据需要,注释掉某个类的相应函数,观察结果。 1. 该例子的继承层次图为:(M和N是虚基类)
2. 代码如下
[cpp] view plaincopyprint?
1. #include
4. class A { 5. public: 6. A() {
7. cout<<\< 9. A(A &a) { 10. cout<<\< 12. A& operator=(A& a) { 13. cout<<\< 16. virtual ~A() { 17. cout<<\< 21. class M :public A { 22. public: 23. M() { 24. cout<<\< 26. M(M &a) { 27. cout<<\< 29. M& operator=(M& m) { 30. cout<<\< 33. virtual ~M() { 34. cout<<\< 38. class B:virtual public M { 39. public: 40. B() { 41. cout<<\< 43. B(B &a) { 44. cout<<\< 46. B& operator=(B& b) { 47. cout<<\< 50. virtual ~B() { 51. cout<<\< 56. class N :public A { 57. public: 58. N() { 59. cout<<\< 60. } 61. N(N &a) { 62. cout<<\< 64. N& operator=(N& n) { 65. cout<<\< 68. virtual ~N() { 69. cout<<\< 72. class C:virtual public N { 73. public: 74. C() { 75. cout<<\< 77. C(C &a) { 78. cout<<\< 80. C& operator=(C& c) { 81. cout<<\< 84. virtual ~C() { 85. cout<<\< 88. class E:virtual public M{ 89. public: 90. E() { 91. cout<<\< 93. E(E &a) { 94. cout<<\< 96. E& operator=(E& e) { 97. cout<<\< 100. virtual ~E() { 101. cout<<\< 104. class D:public B, public C, public E { 105. public: 106. D() { 107. cout<<\< 109. D(D &a) { 110. cout<<\< 112. D& operator=(D& d) { 113. cout<<\< 116. virtual ~D() { 117. cout<<\< 122. int main(int argc, char **argv) { 123. cout<<\构造函数-------\< 125. cout<<\复制构造函数-------\< 127. cout<<\赋值操作符-------\< 129. cout<<\析构函数-------\< 132. return 0; 133. } 3. 运行结果与分析 分析:M和N是虚基类,但是A不是虚基类。B和E共享一个M,但是M和N都会含有类A的部分,因为A不是虚基类,所以M和N不共享A。下面的注释部分为添加的分析。 [cpp] view plaincopyprint? 1. -------构造函数------- 2. int A::A() 3. int M::M()//构造虚基类M时,要先构造其父类A 4. int A::A() 5. int N::N()//和M一样,构造虚基类N时,也要先构造其父类A 6. int B::B()//构造完虚基类,开始构造直接父类,按照声明顺序为B、C、E 7. int C::C() 8. int E::E() 9. int D::D()//最后构造自己 10. -------复制构造函数------- 11. int A::A() 12. int M::M() 13. int A::A() 14. int N::N() 15. int B::B() 16. int C::C() 17. int E::E() 18. int D::D(D &a)//因为D中定义了复制构造函数,并且没有显式调用父类的构造函数,所以所 有的“虚基类”和“直接父类”都调用默认构造函数 19. -------赋值操作符------- 20. int D::operator=(D &a) //因为显式调用了赋值操作符,那么就只调用自己的代码,不会隐 式调用其它的函数 21. -------析构函数------- 22. int D::~D() 23. int E::~E() 24. int C::~C() 25. int B::~B() 26. int N::~N() 27. int A::~A() 28. int M::~M() 29. int A::~A()//因为main函数中定义了两个D对象,所以main函数结束时要进行析构两个D 对象。析构的顺序与 构造函数相反。 30. int D::~D() 31. int E::~E() 32. int C::~C() 33. int B::~B() 34. int N::~N() 35. int A::~A() 36. int M::~M() 37. int A::~A() 38. 39. Press any key to continue. 8. int E::E() 9. int D::D()//最后构造自己 10. -------复制构造函数------- 11. int A::A() 12. int M::M() 13. int A::A() 14. int N::N() 15. int B::B() 16. int C::C() 17. int E::E() 18. int D::D(D &a)//因为D中定义了复制构造函数,并且没有显式调用父类的构造函数,所以所 有的“虚基类”和“直接父类”都调用默认构造函数 19. -------赋值操作符------- 20. int D::operator=(D &a) //因为显式调用了赋值操作符,那么就只调用自己的代码,不会隐 式调用其它的函数 21. -------析构函数------- 22. int D::~D() 23. int E::~E() 24. int C::~C() 25. int B::~B() 26. int N::~N() 27. int A::~A() 28. int M::~M() 29. int A::~A()//因为main函数中定义了两个D对象,所以main函数结束时要进行析构两个D 对象。析构的顺序与 构造函数相反。 30. int D::~D() 31. int E::~E() 32. int C::~C() 33. int B::~B() 34. int N::~N() 35. int A::~A() 36. int M::~M() 37. int A::~A() 38. 39. Press any key to continue.
正在阅读:
C++在单继承、多继承03-13
机电一体化总复习试题及答案05-29
中国人民解放战争历次重要战役全集06-08
教案动物身上会进化出轮子来吗02-01
主要会议内容和简要日程安排11-04
哈尔滨工业大学优秀博士学位论文评选办法03-09
51单片机温度传感器DS18B20程序 LCD1602显示 - 图文05-11
2012农村信用社面试真题含答案05-09
金融学习题集02-03
- 多层物业服务方案
- (审判实务)习惯法与少数民族地区民间纠纷解决问题(孙 潋)
- 人教版新课标六年级下册语文全册教案
- 词语打卡
- photoshop实习报告
- 钢结构设计原理综合测试2
- 2014年期末练习题
- 高中数学中的逆向思维解题方法探讨
- 名师原创 全国通用2014-2015学年高二寒假作业 政治(一)Word版
- 北航《建筑结构检测鉴定与加固》在线作业三
- XX县卫生监督所工程建设项目可行性研究报告
- 小学四年级观察作文经典评语
- 浅谈110KV变电站电气一次设计-程泉焱(1)
- 安全员考试题库
- 国家电网公司变电运维管理规定(试行)
- 义务教育课程标准稿征求意见提纲
- 教学秘书面试技巧
- 钢结构工程施工组织设计
- 水利工程概论论文
- 09届九年级数学第四次模拟试卷
- 继承
- C++
- 艺人代言合同(标准版)
- 关于公寓前期物业管理基本情况和存在问题的汇报
- Delphi2007使用心得
- 杭州珍爱网-父母不满意女朋友怎么办
- java程序打包成jar文件
- 2019年整理--林机社区党支部工作总结范文
- 企业金融会计风险防范与化解
- 草房子习题及答案
- New Zealands Tourism
- 基础工程施工课程设计指导书
- 《赶圩归来啊哩哩》教学设计及点评
- 新人教版小学二年级上册数学全册教学反思
- 湖州南浔织里天天室内设计装潢
- 美军C4ISR的形成与发展
- 高空作业安全要求
- “不忘初心 牢记使命”演讲稿:不忘初心 砥砺前行
- stata回归分析完整步骤-吐血推荐
- 青年教师讲课比赛心得体会
- 医学生实习心得3篇
- 2019-2020年苏少版音乐五下奏《瑶族舞曲》教学设计