先初始化序列中的函数调用,如果基类构造函数为非引用传递,则引起参数的拷贝构造

再: 先类内的成员构造函数(拷贝/默认),再类的构造函数;先基类,再派生类;

本文主要说明对象创建时构造函数的执行顺序,对象成员的初始化顺序;对象销毁时析构函数的执行顺序,对象成员的销毁顺序。

“对象的构造从类层次的最根处开始,在每一层中,首先调用基类的构造函数,然后调用成员对象的构造函数。析构则严格按照与构造相反的次序执行,该次序是唯一的,否则编译器将无法自动执行析构过程。

一个有趣的现象是,成员对象初始化的次序完全不受它们在初始化表中次序的影响, 只由成员对象在类中声明的次序决定。这是因为类的声明是唯一的,而类的构造函数可以有多个,因此会有多个不同次序的初始化表。如果成员对象按照初始化表的次序进行构造,这将导致析构函数无法得到唯一的逆序。”(引用自References[1])

从这里看,每种语言特性的存在必有其原因,学习这些特性就是理解这些特性存在的原因。

下面的一段代码是对上面这段话的说明,其中有4个类Foo, Bar, Base, Derived,它们的构造函数、拷贝构造函数、析构函数都有信息输出。

  1. #Filename: ConstructSequence.cc
  2. #include <iostream>
  3. using namespace std;
  4. class Foo
  5. {
  6. public:
  7. Foo() { cout << "Foo constructor" << endl; }
  8. Foo(const Foo &foo) { cout << "Foo copy constructor" << endl; }
  9. ~Foo() { cout << "Foo deconstructor" << endl; }
  10. };
  11. class Bar
  12. {
  13. public:
  14. Bar() { cout << "Bar constructor" << endl; }
  15. Bar(const Bar &bar) { cout << "Bar copy constructor" << endl; }
  16. ~Bar() { cout << "Bar deconstructor" << endl; }
  17. };
  18. class Base
  19. {
  20. public:
  21. Base() { cout << "Base constructor" << endl; }
  22. ~Base() { cout << "Base deconstructor" << endl; }
  23. };
  24. class Derived : public Base
  25. {
  26. public:
  27. Derived() { cout << "Derived constructor without arguments" << endl; }
  28. Derived(const Foo &foo, const Bar &bar);
  29. Derived(const Bar &bar, const Foo &foo);
  30. ~Derived() { cout << "Derived deconstructor" << endl; }
  31. private:
  32. Foo m_foo;
  33. Bar m_bar;
  34. };
  35. Derived::Derived(const Foo &foo, const Bar &bar) :
  36. m_foo(foo),
  37. m_bar(bar)
  38. {
  39. cout << "Derived constructor with argument[Foo foo, Bar bar] passed by references" << endl;
  40. }
  41. Derived::Derived(const Bar &bar, const Foo &foo) :
  42. m_bar(bar),
  43. m_foo(foo)
  44. {
  45. cout << "Derived constructor with argument[Bar bar, Foo foo] passed by references" << endl;
  46. }
  47. int main (int argc, char** argv)
  48. {
  49. Foo foo;
  50. Bar bar;
  51. cout << "test case 1:" << endl;
  52. Derived deri_1;  //  (1)
  53. cout << "test case 2:" << endl;
  54. Derived deri_2(foo, bar);   //  (2)
  55. cout << "test case 3:" << endl;
  56. Derived deri_3(bar, foo);   //  (3)
  57. cout << "test case end" << endl;
  58. return 0;
  59. }

执行结果是:

打印出的信息可分为几部分:

(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。

编译运行环境:

  1. $ uname -a
  2. 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
  3. $ g++ --version
  4. 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++-理解构造函数、析构函数执行顺序的更多相关文章

  1. C# 父子类_实例_静态成员变量_构造函数的执行顺序

    今天去面试的时候被一道题问得一点脾气都没有,今天特地来研究下. 子类成员变量,子类静态成员变量,子类构造函数,父类成员变量,父类静态成员变量,父类构造函数的执行顺序. 现在贴上从另外一个.net程序员 ...

  2. Java的初始化块、静态初始化块、构造函数的执行顺序及用途探究

    Java与C++有一个不同之处在于,Java不但有构造函数,还有一个”初始化块“(Initialization Block)的概念.下面探究一下它的执行顺序与可能的用途. 执行顺序 首先定义A, B, ...

  3. 0708关于理解mysql SQL执行顺序

    转自 http://www.jellythink.com/archives/924,博客比价清晰 我理解上文的是SQL执行顺序 总体方案.当你加入索引了以后,其实他的执行计划是有细微的变化,比方说刚开 ...

  4. Java组合与继承生成的类中构造函数的执行顺序

    [程序实例] import java.util.*; class Meal{ Meal() { System.out.println("Meal Constructor"); } ...

  5. C++构造函数和析构函数执行顺序

    四种情况:1. 创建一个类指针时,调用其构造函数:删除当前指针时,自动调用其析构函数.2. 创建子类对象指针时,首先调用其父类的构造函数,然后调用子类的构造函数:删除当前指针时先调用子类的析构函数,然 ...

  6. c++派生类中构造函数和析构函数执行顺序、判断对象类型、抽象类、虚函数

    一. 代码: 1 #include<stdio.h> 2 #include<string.h> 3 #include<algorithm> 4 #include&l ...

  7. C#基础知识之父子类,实例、静态成员变量,构造函数的执行顺序(经典示例)

    父子类.示例.静态成员变量.构造函数的概念的基础理解完全可以利用下面的示例诠释,非常经典,直接上代码: public class ShowInfo { public ShowInfo(string i ...

  8. Java基础 静态块、非静态块、构造函数的执行顺序

    Java中经常有一些静态块,这是用来在生成类之前进行的初始化,无论java还C++语言中的static,都是最先初始化好的.结构如下: static { 静态语句代码块 } { 非静态语句代码块 }  ...

  9. java子类和父类中静态块、非静态块、构造函数的执行顺序

    public class qqqq extends Parent{ public static void main(String[] args) { new Child(); } } class Pa ...

随机推荐

  1. Linux mysql常用操作命令

    原文:http://blog.csdn.net/lifuxiangcaohui/article/details/46375931 1.linux下启动MySQL的命令:mysqladmin start ...

  2. QBC用法

    方法 说明 Restrictions.eq = Restrictions.allEq 利用Map来进行多个等于的限制 Restrictions.gt > Restrictions.ge > ...

  3. 【服务器环境搭建-Centos】系统分区 待续

    df命令查看,显示/dev/vda,而不是sda或hda ,为什么? 虚拟机为了提升性能,一般使用virtio作为磁盘驱动,在虚拟机里面磁盘会显示成vda而不是传统的sda,有什么办法可以让他显示成s ...

  4. listToString

    http://www.oschina.net/code/snippet_109648_2229

  5. Python装饰模式实现源码分享

    1.一般来说,通过继承可以获得父类的属性,还可以通过重载修改其方法. 2.装饰模式可以不以继承的方式而动态地修改类的方法. 3.装饰模式可以不以继承的方式而返回一个被修改的类. 4.基本实现 程序演示 ...

  6. 按钮靠右css小结

    按钮靠右 style="float:right"  ,多按钮排版会相反 按钮内的字体靠右 style="text-align:right" 按钮离右边框距离 s ...

  7. 机器学习与R语言

    此书网上有英文电子版:Machine Learning with R - Second Edition [eBook].pdf(附带源码) 评价本书:入门级的好书,介绍了多种机器学习方法,全部用R相关 ...

  8. python2 urllib 笔记

    python2 urllib 笔记 import urllib base='http://httpbin.org/' ip=base+'ip' r=urllib.urlopen(ip) print r ...

  9. 使用node js 操作 Mysql 数据库

    使用node js 操作 Mysql 数据库 http://www.nodejs.org/ //node js 数据库操作 MySQL //使用https://github.com/felixge/n ...

  10. 串行通讯之UARTLoopback

    目录 第1章串行通讯之UARTLoopback    2 1 USB转串口    2 2 USB Accessory    2 3 连入手机    3 4 代码改进    4 5 打开串口    4 ...