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 ...
随机推荐
- Nginx 启用gzip压缩
1. 网页压缩 网页压缩是一项由 WEB 服务器和浏览器之间共同遵守的协议,也就是说 WEB 服务器和浏览器都必须支持该技术,所幸的是现在流行的浏览器都是支持的,包括 IE.FireFox.Opera ...
- 转!!MAC和PHY的区别
一块以太网网卡包括OSI(开方系统互联)模型的两个层,物理层和数据链路层.物理层定义了数据传送与接收所需要的电与光信号.线路状态.时钟基准.数据编码和电路等,并向数据链路层设备提供标准接口.数据链路层 ...
- 基因组 de novo 组装原理
Falcon软件的组装流程 为了错误校正,将原始子reads进行overlap 预组装和错误校正 错误校正后reads的overlap检测 overlap的过滤 从overlap构建图 从图构建con ...
- xcode6 devices,profiles 以及 iOS8 设备 查看profiles
xcode6 devices,profiles 以及 iOS8 设备 查看profiles 1. xocde6 devices 不在 window----Organizer 下面了: 改为 Windo ...
- 制作Aspose CHM文档的过程记录
欢迎和大家交流技术相关问题: 邮箱: jiangxinnju@163.com 博客园地址: http://www.cnblogs.com/jiangxinnju GitHub地址: https://g ...
- 【CodeForces 651B】Beautiful Paintings 排序+贪心
题目大意: 给定集合,对于任意一个的排列,记,求. 很明显每次搞出一个长度为的最长上升序列,然后把元素给删掉,答案增加. 直接暴力需要. 但是可以进行优化. 设有个,将个数从小到大排序,记为长度为的数 ...
- font-face字体文件引入方式
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8&quo ...
- CentOS 6.x安装配置
简述 VMware可以创建多个虚拟机,每个虚拟机上都可以安装各种类型的操作系统.安装方法也有很多种.下面,主要以ISO镜像安装为例,介绍CentOS 6.x的安装过程及相关的参数设置. 简述 创建虚拟 ...
- C++数据类型范围
C++中有很多基本的数据类型,我们在使用过程中需要根据所需要存储数据的范围的不同而选择恰当的数据类型. Visual C++ 32 位和 64 位编译器可识别本文后面的表中的类型. int (unsi ...
- DIV使用tabindex获得事件详解 移动div
添加 tabindex='-1' 属性: 默认:获取不到焦点事件(blur) 1 <div class="wl-product" id="wl-product&qu ...