C++-理解构造函数、析构函数执行顺序
先初始化序列中的函数调用,如果基类构造函数为非引用传递,则引起参数的拷贝构造
再: 先类内的成员构造函数(拷贝/默认),再类的构造函数;先基类,再派生类;
本文主要说明对象创建时构造函数的执行顺序,对象成员的初始化顺序;对象销毁时析构函数的执行顺序,对象成员的销毁顺序。
“对象的构造从类层次的最根处开始,在每一层中,首先调用基类的构造函数,然后调用成员对象的构造函数。析构则严格按照与构造相反的次序执行,该次序是唯一的,否则编译器将无法自动执行析构过程。
一个有趣的现象是,成员对象初始化的次序完全不受它们在初始化表中次序的影响, 只由成员对象在类中声明的次序决定。这是因为类的声明是唯一的,而类的构造函数可以有多个,因此会有多个不同次序的初始化表。如果成员对象按照初始化表的次序进行构造,这将导致析构函数无法得到唯一的逆序。”(引用自References[1])
从这里看,每种语言特性的存在必有其原因,学习这些特性就是理解这些特性存在的原因。
下面的一段代码是对上面这段话的说明,其中有4个类Foo, Bar, Base, Derived,它们的构造函数、拷贝构造函数、析构函数都有信息输出。
- #Filename: ConstructSequence.cc
 - #include <iostream>
 - using namespace std;
 - class Foo
 - {
 - public:
 - Foo() { cout << "Foo constructor" << endl; }
 - Foo(const Foo &foo) { cout << "Foo copy constructor" << endl; }
 - ~Foo() { cout << "Foo deconstructor" << endl; }
 - };
 - class Bar
 - {
 - public:
 - Bar() { cout << "Bar constructor" << endl; }
 - Bar(const Bar &bar) { cout << "Bar copy constructor" << endl; }
 - ~Bar() { cout << "Bar deconstructor" << endl; }
 - };
 - class Base
 - {
 - public:
 - Base() { cout << "Base constructor" << endl; }
 - ~Base() { cout << "Base deconstructor" << endl; }
 - };
 - class Derived : public Base
 - {
 - public:
 - Derived() { cout << "Derived constructor without arguments" << endl; }
 - Derived(const Foo &foo, const Bar &bar);
 - Derived(const Bar &bar, const Foo &foo);
 - ~Derived() { cout << "Derived deconstructor" << endl; }
 - private:
 - Foo m_foo;
 - Bar m_bar;
 - };
 - Derived::Derived(const Foo &foo, const Bar &bar) :
 - m_foo(foo),
 - m_bar(bar)
 - {
 - cout << "Derived constructor with argument[Foo foo, Bar bar] passed by references" << endl;
 - }
 - Derived::Derived(const Bar &bar, const Foo &foo) :
 - m_bar(bar),
 - m_foo(foo)
 - {
 - cout << "Derived constructor with argument[Bar bar, Foo foo] passed by references" << endl;
 - }
 - int main (int argc, char** argv)
 - {
 - Foo foo;
 - Bar bar;
 - cout << "test case 1:" << endl;
 - Derived deri_1; // (1)
 - cout << "test case 2:" << endl;
 - Derived deri_2(foo, bar); // (2)
 - cout << "test case 3:" << endl;
 - Derived deri_3(bar, foo); // (3)
 - cout << "test case end" << endl;
 - return 0;
 - }
 
执行结果是:

打印出的信息可分为几部分:
(1) 创建对象foo和bar ,执行Foo,Bar的构造函数
(2) Test Case 1:创建对象deri_1,首先执行基类Base的构造函数,其次执行成员m_foo,m_bar的构造函数来构造成员,最后调用自身的构造函数(无参数)。
(3) Test Case 2:创建对象deri_2,调用顺序与case 1顺序相同。
(4) Test Case 3:创建对象 deri_3,调用顺序与case 1顺序相同。注意到deri_2,deri_3的创建执行的是不同的Derived构造函数,虽然构造函数参数的顺序不同,但是构造成员的顺序是相同的。
(5) 销毁对象deri_3,deri_2,deri_1,析构函数执行顺序相同,与构造对象的顺序相反。C++标准规定以对象声明相反的顺序销毁这些对象。
(6) 销毁对象bar,foo。
编译运行环境:
- $ uname -a
 - Linux localhost.localdomain 2.6.18-308.el5 #1 SMP Fri Jan 27 17:17:51 EST 2012 x86_64 x86_64 x86_64 GNU/Linux
 - $ g++ --version
 - g++ (GCC) 4.1.2 20080704 (Red Hat 4.1.2-52)
 
References:
[1]高质量C++编程指南: http://oss.org.cn/man/develop/c&c++/c/c.htm
[2] http://stackoverflow.com/q/15948381/1145750
转载本文请注明作者和出处[Gary的影响力]http://garyelephant.me,请勿用于任何商业用途!
C++-理解构造函数、析构函数执行顺序的更多相关文章
- C# 父子类_实例_静态成员变量_构造函数的执行顺序
		
今天去面试的时候被一道题问得一点脾气都没有,今天特地来研究下. 子类成员变量,子类静态成员变量,子类构造函数,父类成员变量,父类静态成员变量,父类构造函数的执行顺序. 现在贴上从另外一个.net程序员 ...
 - Java的初始化块、静态初始化块、构造函数的执行顺序及用途探究
		
Java与C++有一个不同之处在于,Java不但有构造函数,还有一个”初始化块“(Initialization Block)的概念.下面探究一下它的执行顺序与可能的用途. 执行顺序 首先定义A, B, ...
 - 0708关于理解mysql SQL执行顺序
		
转自 http://www.jellythink.com/archives/924,博客比价清晰 我理解上文的是SQL执行顺序 总体方案.当你加入索引了以后,其实他的执行计划是有细微的变化,比方说刚开 ...
 - Java组合与继承生成的类中构造函数的执行顺序
		
[程序实例] import java.util.*; class Meal{ Meal() { System.out.println("Meal Constructor"); } ...
 - C++构造函数和析构函数执行顺序
		
四种情况:1. 创建一个类指针时,调用其构造函数:删除当前指针时,自动调用其析构函数.2. 创建子类对象指针时,首先调用其父类的构造函数,然后调用子类的构造函数:删除当前指针时先调用子类的析构函数,然 ...
 - c++派生类中构造函数和析构函数执行顺序、判断对象类型、抽象类、虚函数
		
一. 代码: 1 #include<stdio.h> 2 #include<string.h> 3 #include<algorithm> 4 #include&l ...
 - C#基础知识之父子类,实例、静态成员变量,构造函数的执行顺序(经典示例)
		
父子类.示例.静态成员变量.构造函数的概念的基础理解完全可以利用下面的示例诠释,非常经典,直接上代码: public class ShowInfo { public ShowInfo(string i ...
 - Java基础  静态块、非静态块、构造函数的执行顺序
		
Java中经常有一些静态块,这是用来在生成类之前进行的初始化,无论java还C++语言中的static,都是最先初始化好的.结构如下: static { 静态语句代码块 } { 非静态语句代码块 } ...
 - java子类和父类中静态块、非静态块、构造函数的执行顺序
		
public class qqqq extends Parent{ public static void main(String[] args) { new Child(); } } class Pa ...
 
随机推荐
- Linux下的split 命令(将一个大文件根据行数平均分成若干个小文件)
			
将一个大文件分成若干个小文件方法 例如将一个BLM.txt文件分成前缀为 BLM_ 的1000个小文件,后缀为系数形式,且后缀为4位数字形式 先利用 wc -l BLM.txt 读出 BL ...
 - 测试分页查询出数据并分文件导出[java工程]
			
package cn.shiyanjun.test; import java.util.ArrayList; import java.util.List; public class ExcelTest ...
 - hiho_1049 二叉树遍历
			
题目大意 给出一棵二叉树的前序和中序遍历结果,求出后序遍历的结果.保证二叉树中节点值均不相同. 分析 通过前序和中序遍历的结果,我们可以构建出二叉树,若构建出二叉树,则后序遍历的结果很容易求出(当然递 ...
 - css编码规范
			
css编码规范 https://segmentfault.com/a/1190000002460968 常用样式测试工具 W3C CSS validator:http://jigsaw.w3.org/ ...
 - php 执行外部命令exec() system() passthru()
			
php 执行部命令exec() system() passthru() 通常用c写一个外部小程序,然后使用上述命令可以在php中调用 1. exec() string exec ( string $c ...
 - B2车
			
晚上10点之后,杭州快速公交B2的司机,把2节车厢,开的像是跑车一样,每次启动都是弹射出去的,好像是在报复白天蜗牛般的速度.真乃是见神杀神,见佛杀佛.
 - php_curl.dll libssh2.dll 始终无法加载的原因 及解决办法
			
在StackOverflow得到最终原因及解决办法 http://stackoverflow.com/questions/16424117/php-unable-to-load-php-curl-dl ...
 - [JS] 限制上传文件的类型和大小
			
<!DOCTYPE html> <!-- saved from url=(0035)http://localhost:9090/qraved/update --> <ht ...
 - 第二周  SCRUM站立会议
			
站立会议是成员间每个人面对面站立着说出自己的进展,不是会议,不是写报告.是为了更好的沟通和协调,本质上是为了工程方面的团队交流. scrum站立会议的要求如下: 1.成员间都是平等的,发言没有经理和程 ...
 - linux开关端口问题
			
linux开关端口问题: 我们知道一些常用的端口,比如mysql的端口为3306,sql的端口为:1433,以及tomcat的端口为 8008等等一样! 当这些端口在linux下是没有开启时,我们是无 ...