C++:派生类的构造函数和析构函数的调用顺序

一、派生类

在C++编程中,我们在编写一个基类的派生类时,大致可以分为四步

? 吸收基类的成员:不论是数据成员还是函数成员,派生类吸收除基类的构造函数和析构函数之外的全部成员。

? 改造基类函数:在派生类中声明一个或多个与其(某个)基类中的成员函数同名的成员函数,并将它(们)根据新的需求进行重写

? 发展新的成员:在派生类中添加新的成员变量和成员函数,其中新添加的成员要求必须和基类中的成员不同名,并且应当保证新添加的成员会使派生类在功能上相比其基类有所发展

? 重写派生类的构造函数和析构函数

特别注意:

在重写派生类的构造函数时,通常将其基类们的构造函数变成新构造函数的一部分

语法:派生类名::派生类名(参数总表):基类名1(参数表1),基类名2(参数表2),......,新增成员名1(参数1),新增成员名2(参数2){}

示例:

 1 #include<iostream>
 2 using namespace std;
 3 class Father{ //基类
 4 public:
 5     Father()=default; //默认构造函数
 6     Father(int value):father_value(value){} //带参数的构造函数
 7     ~Father(){} //析构函数
 8     void show(){
 9         cout<<"这是基类Father"<<endl;
10     }
11     void father_func(){
12         cout<<"这是基类Father的方法father_func:"<<endl;
13         cout<<"\t成员father_value的值为:"<<father_value<<endl;
14     }
15 private:
16     int father_value;
17 };
18
19 class Son:public Father{ //Father类的派生类Son
20 public:
21     /*步骤一:吸收Father类中除构造函数和析构函数之外的所有成员
22          void show(){
23             cout<<"这是基类Father"<<endl;
24         }
25         void father_func(){
26             cout<<"这是基类Father的方法father_func:"<<endl;
27             cout<<"\t成员father_value的值为:"<<father_value<<endl;
28         }
29     */
30     //步骤二:改造基类Father中的成员
31     void show(){
32         cout<<"这是派生类Son"<<endl;
33     }
34     //步骤三:发展新的成员
35     void son_func(){
36         cout<<"这是派生类Son的方法son_func: "<<endl;
37         cout<<"\t成员son_value的值为:"<<son_value<<endl;
38     }
39     //步骤四:重写构造函数和析构函数
40     Son()=default;
41     Son(int value):Father(value),son_value(value){}
42     ~Son(){}
43 private:
44     /*步骤一:吸收Father类中除构造函数和析构函数之外的所有成员
45         int father_value;
46     */
47     //步骤三:发展新的成员
48     int son_value;
49 };
50 int main(){
51     Father father(10);
52     father.show();
53     father.father_func();
54     cout<<"------------分界线--------------------"<<endl;
55     Son son(20);
56     son.show();
57     son.father_func();
58     son.son_func();
59     return 0;
60 }

二、派生类的构造函数的调用顺序

我们先来看一个示例:

 1 #include<iostream>
 2 using namespace std;
 3 class Father1{ //基类1
 4 public:
 5     Father1(){
 6         cout<<"这是Father1类的构造函数"<<endl;
 7     }
 8 };
 9
10 class Father2{ //基类2
11 public:
12     Father2(){
13         cout<<"这是Father2类的构造函数"<<endl;
14     }
15 };
16
17 class Father3{ //基类3
18 public:
19     Father3(){
20         cout<<"这是Father3类的构造函数"<<endl;
21     }
22 };
23
24 class Son:public Father1,public Father2,public Father3{ //派生类
25 public:
26     Son(){
27         cout<<"这是Son类的构造函数"<<endl;
28     }
29 };
30
31 int main(){
32     Son s;
33     return 0;
34 }

由上面的例子可以看出,派生类在创建对象时会先调用其基类们的构造函数,然后才会调用自己的构造函数。下面是类Son的对象s在内存中的存放形式:

那么派生类调用基类的构造函数的顺序又是如何确定的呢?我们在来看一个例子:

 1 #include<iostream>
 2 using namespace std;
 3 class Father1{ //基类1
 4 public:
 5     Father1(){
 6         cout<<"这是Father1类的构造函数"<<endl;
 7     }
 8 };
 9
10 class Father2{ //基类2
11 public:
12     Father2(){
13         cout<<"这是Father2类的构造函数"<<endl;
14     }
15 };
16
17 class Father3{ //基类3
18 public:
19     Father3(){
20         cout<<"这是Father3类的构造函数"<<endl;
21     }
22 };
23
24 class Son:public Father3,public Father1,public Father2{ //派生类
25 public:
26     Son(){
27         cout<<"这是Son类的构造函数"<<endl;
28     }
29 private:
30     Father1 father1;
31     Father2 father2;
32     Father3 father3;
33 };
34
35 int main(){
36     Son s;
37     return 0;
38 }

由此可见,派生类在创建对象时其调用构造函数的顺序是:

? 先按照派生类对基类的继承顺序调用基类的构造函数。上例中由语句“class Son:public Father3,public Father1,public Father2”可知类Son先继承基类Father3,然后继承基类Father1,最后继承基类Father2,因此其调用基类的构造函数的顺序也是先Fathe3,再Father1,最后Father2。

?( 若派生类的成员变量中存在其基类的对象 )接着按照基类的对象在派生类定义中声明的先后顺序调用基类的构造函数。上例中按照成员对象的定义顺序依次调用Father1、Father2和Father3的构造函数。

? 最后调用派生类自己的构造函数

特别注意:

1.如果基类中没有定义默认构造函数或带有缺省值的构造函数而只有带参数的构造函数时,派生类的构造函数中必须显式的给出基类名和参数表,否则编译器将报错

 1 #include<iostream>
 2 using namespace std;
 3 class Father1{ //基类1
 4 public:
 5     Father1(int v):value1(v){
 6         cout<<"这是Father1类的构造函数"<<endl;
 7     }
 8 private:
 9     int value1;
10 };
11
12 class Father2{ //基类2
13 public:
14     Father2(int v):value2(v){
15         cout<<"这是Father2类的构造函数"<<endl;
16     }
17 private:
18     int value2;
19 };
20
21 class Father3{ //基类3
22 public:
23     Father3(int v):value3(v){
24         cout<<"这是Father3类的构造函数"<<endl;
25     }
26 private:
27     int value3;
28 };
29
30 class Son:public Father1,public Father2,public Father3{ //派生类
31 public:
32     Son(int v):Father1(v),Father2(v),Father3(v),value4(v){ //派生类的构造函数中必须显式的给出基类名和参数表
33         cout<<"这是Son类的构造函数"<<endl;
34     }
35 private:
36     int value4;
37 };
38
39 int main(){
40     Son s(10);
41     return 0;
42 }

2.如果基类中没有定义构造函数,这派生类也可以不定义构造函数,系统会自动在类中添加默认的构造函数的

3.如果基类中定义了带有参数表的构造函数时,派生类就应当定义相应的构造函数

QUESTION:基类在派生类的构造函数的初始化列表中的顺序是否会影响派生类的构造函数调用顺序?

ANSWER: 我们先来看一个示例:

 1 #include<iostream>
 2 using namespace std;
 3 class Father1{ //基类1
 4 public:
 5     Father1(int v):value1(v){
 6         cout<<"这是Father1类的构造函数"<<endl;
 7     }
 8 private:
 9     int value1;
10 };
11
12 class Father2{ //基类2
13 public:
14     Father2(int v):value2(v){
15         cout<<"这是Father2类的构造函数"<<endl;
16     }
17 private:
18     int value2;
19 };
20
21 class Father3{ //基类3
22 public:
23     Father3(int v):value3(v){
24         cout<<"这是Father3类的构造函数"<<endl;
25     }
26 private:
27     int value3;
28 };
29
30 class Son:public Father3,public Father1,public Father2{ //派生类
31 public:
32     Son(int v):Father1(v),Father2(v),Father3(v),value4(v){
33         cout<<"这是Son类的构造函数"<<endl;
34     }
35 private:
36     int value4;
37 };
38
39 int main(){
40     Son s(10);
41     return 0;
42 }

上面的例子中派生类Son是以顺序Father3、Father1、Father2来继承基类的,但在其构造函数中基类的顺序确实Father1、Father2、Father3。最终的结果表明类Son仍以其继承基类的顺序来调用基类的构造函数,而非基类在派生类构造函数中的顺序。因此可见基类在派生类的构造函数的初始化列表中的顺序不会影响派生类的构造函数调用顺序

三、派生类的析构函数的调用顺序

? 在派生类中,其析构函数只需要关心新增的一般成员的“善后工作”。而对于新增的成员对象和基类的“善后工作”,系统会自己调用成员对象和基类的析构函数来完成,而不需要用户来关心。

? 在派生类中,析构函数各部分的执行顺序与其构造函数的调用顺序刚好相反,即派生类的析构函数先对其新增的一般成员进行析构,然后对新增的成员对象进行析构,最后按照与其继承基类的相反顺序来调用基类的析构函数。

 1 #include<iostream>
 2 using namespace std;
 3 class Father1{ //基类1
 4 public:
 5     Father1(){
 6         cout<<"这是Father1类的构造函数"<<endl;
 7     }
 8     ~Father1(){
 9         cout<<"这是Father1类的析构函数"<<endl;
10     }
11 };
12
13 class Father2{ //基类2
14 public:
15     Father2(){
16         cout<<"这是Father2类的构造函数"<<endl;
17     }
18     ~Father2(){
19         cout<<"这是Father2类的析构函数"<<endl;
20     }
21 };
22
23 class Father3{ //基类3
24 public:
25     Father3(){
26         cout<<"这是Father3类的构造函数"<<endl;
27     }
28     ~Father3(){
29         cout<<"这是Father3类的析构函数"<<endl;
30     }
31 };
32
33 class Son:public Father3,public Father1,public Father2{ //派生类
34 public:
35     Son(){
36         cout<<"这是Son类的构造函数"<<endl;
37     }
38     ~Son(){
39         cout<<"这是Son类的析构函数"<<endl;
40     }
41 private:
42     Father1 father1;
43     Father2 father2;
44     Father3 father3;
45 };
46
47 int main(){
48     Son s;
49     return 0;
50 }

时间: 2024-12-11 18:50:59

C++:派生类的构造函数和析构函数的调用顺序的相关文章

c++学习笔记5,多重继承中派生类的构造函数与析构函数的调用顺序(二)

现在来测试一下在多重继承,虚继承,MI继承中虚继承中构造函数的调用情况. 先来测试一些普通的多重继承.其实这个是显而易见的. 测试代码: //测试多重继承中派生类的构造函数的调用顺序何时调用 //Fedora20 gcc version=4.8.2 #include <iostream> using namespace std; class base { public: base() { cout<<"base created!"<<endl; }

c++学习笔记4,派生类的构造函数与析构函数的调用顺序(一)

測试源代码: //測试派生类的构造函数的调用顺序何时调用 //Fedora20 gcc version=4.8.2 #include <iostream> using namespace std; class base { public: base() { cout<<"base created!"<<endl; } ~base() { cout<<"base destroyed!"<<endl; } };

派生类的构造函数与析构函数的调用顺序

派生类构造函数各部分的执行次序为1.调用基类的构造函数,按他们在派生类定义的先后顺序,顺序调用.2.调用成员对象的构造函数,按他们在类定义中声明的先后顺序,顺序调用3.派生类的构造函数体中的操作 在派生类构造函数中,只要基类不是使用缺省构造函数,都要显式给出基类名和参数表如果基类没有定义构造函数,则派生类也可以不定义,全部采用系统给定的缺省构造函数.如果基类定义了带有形参表的构造函数时,派生类就应当定义构造函数. //Test1.h #include<iostream> using names

C++基类和派生类的构造函数和析构函数的调用

C++基类和派生类的构造函数和析构函数的调用 1.调用顺序 当创建一个派生类的对象时,系统首先自动创建一个基类对象,也就是说,在调用派生类构造函数创建派生类对象之前,系统首先调用基类的构造函数创建基类对象.当派生类对象生命期结束时,首先调用派生类的析构函数,然后调用基类的析构函数. 所以,构造函数:基类->派生类:析构函数:派生类->基类. 示例: 1 #include<iostream> 2 using namespace std; 3 4 class A 5 { 6 publi

类的构造函数与析构函数的调用顺序

先来段代码: 1 #include <iostream> 2 using namespace std; 3 4 class Clock{ 5 private: 6 int H,M,S; 7 public: 8 Clock(int h=0,int m=0,int s=0){ 9 H=h,M=m,S=s; 10 cout<<"constructor:"<<H<<":"<<M<<":&quo

C++:派生类的构造函数和析构函数

4.2 派生类的构造函数和析构函数4.2.1 派生类构造函数和析构函数的执行顺序 通常情况下,当创建派生类对象时,首先执行基类的构造函数,随后再执行派生类的构造函数:当撤销派生类对象时,则先执行派生类的派生类的析构函数,随后再执行基类的析构函数. //例4.5 派生类的构造函数和析构函的执行顺序 #include<iostream> using namespace std; class Base{ //声明基类Base public: Base() { cout<<"Co

对C++中派生类的构造函数和析构函数的认识

一:构造函数 形式:派生类名::派生类名:基类名1(参数1),基类名2(参数2),--基类名n(参数n),数据成员1(参数1),数据成员2(参数2),--数据成员n(参数n){ 各种操作的说明 } 执行过程:先执行基类的构造函数,再进行数据成员的赋值,最后执行函数体. 其中基类名和数据成员的顺序是由在派生类的定义中声明的顺序决定执行的顺序的,因此它们的顺序是任意的,但为了可读性,还是最好按顺序写. 如果基类只有默认构造函数,则基类名和参数表可以不用写出来. 二:复制构造函数 派生类的构造函数的形

C++C++中构造函数与析构函数的调用顺序

http://blog.csdn.net/xw13106209/article/details/6899370 1.参考文献 参考1: C++继承中构造函数.析构函数调用顺序及虚函数的动态绑定 参考2: 构造函数.拷贝构造函数和析构函数的的调用时刻及调用顺序 参考3: C++构造函数与析构函数的调用顺序 2.构造函数.析构函数与拷贝构造函数介绍 2.1构造函数 构造函数不能有返回值 缺省构造函数时,系统将自动调用该缺省构造函数初始化对象,缺省构造函数会将所有数据成员都初始化为零或空 创建一个对象

【C/C++】构造函数与析构函数的调用顺序

常见问题 Q1. 以下代码的输出结果是什么? 1 #include <iostream> 2 using namespace std; 3 4 class A 5 { 6 public: 7 A() { cout << "A" << endl; } 8 ~A() { cout << "~A" << endl; } 9 }; 10 class B : public A 11 { 12 public: 13 B