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 ...
随机推荐
- 让Fiddler能够检测到localhost的http数据
用 vs.net开发调试网站程序时经常有这样的地址: http://localhost:2033/ 然而在开启 Fiddler 后会发现Fiddler 完全抓不到任何封包. 主要的原因是因为 Fidd ...
- JMX 简介 和 实例
What is JMX? 10 mins Quick Start JMX Tutorial 这篇文章作为入门,非常清楚
- 双心ping GUI工具1.0
双心ping GUI工具1.0该软件利用WindowsAPI提供了图形界面的ping程序,同时还可以调用DOS下的ping命令.ping成功后自动加入网址列表框及同目录下的列表文件Pinglist.i ...
- jquery 学习
地址:http://www.w3school.com.cn/jquery/jquery_dom_add.asp
- Beautiful Soup教程 转
Python中使用Beautiful Soup库的超详细教程 转 http://www.jb51.net/article/65287.htm 作者:崔庆才 字体:[增加 减小] 类型:转载 时间:20 ...
- struts2 mybatis spring hibernate 框架 pom.xml配置 下载地址
访问以下地址:搜索需要框架的配置 选择需要的版本 http://mvnrepository.com
- Mybatis 学习-1
本次MyBatis基础实例教程主要讲述MyBatis在项目中的配置方法,实体对象的关系映射.关联关系,以及如何实现一个BaseDao的功能 实例数据库的表结构 CREATE TABLE `blog_u ...
- 关于java中JButton的样式设置(的一些我们应该知道的函数)(转)
1. 对JButton大小的设置 ——因为JButen是属于小器件类型的,所以一般的setSize不能对其惊醒大小的设置,所以一般我们用 button.setPreferredSize(new Dim ...
- eclipse 中提示tomcat 的端口被占用了 后的最快捷解决方法
很多时候运行tomcat 的时候总是会提示tomcat 的端口被占用 但是任务管理器里面还找不到是哪个端口被占用了 因此很多人就重新配置tomcat 或者去修改tomcat的端口号 ,其实这么做太麻 ...
- iframe-摘自网友
定义和用法 iframe 元素会创建包含另外一个文档的内联框架(即行内框架). HTML 与 XHTML 之间的差异 在 HTML 4.1 Strict DTD 和 XHTML 1.0 Strict ...