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 ...
随机推荐
- maven编译项目时提示:cached in the local repository
今天使用命令mvn compile编译maven项目时提示错误信息,部分错误信息如下: ...... was cached in the local repository, resolution wi ...
- JAVA调用C语言写的SO文件
JAVA调用C语言写的SO文件 因为工作需要写一份SO文件,作为手机硬件IC读卡和APK交互的桥梁,也就是中间件,看了网上有说到JNI接口技术实现,这里转载了一个实例 // 用JNI实现 // 实例: ...
- 解决SQL命令行回退的问题
场景 在linux或者aix上安装后Oracle后,在SQL命令行下无法通过键盘的退格键回退,如下 解决方法 安装软件 # rpm -ivh rlwrap-0.41-1.el6.x86_64.rpm ...
- shell脚本中获取本机ip地址的方法
ipaddr='172.0.0.1' ipaddr=$(ip addr | awk '/^[0-9]+: / {}; /inet.*global/ {print gensub(/(.*)\/(.*)/ ...
- Source Insight 中使用 AStyle 代码格式工具
Source Insight 中使用 AStyle 代码格式工具 彭会锋 2015-05-19 23:26:32 Source Insight是较好的代码阅读和编辑工具,不过source in ...
- 《javascript高级程序设计》第21章 Ajax和Comet
21.1 XMLHttpRequest 对象 The XMLHttpRequest Object 21.1.1 XHR 的用法 XHR Usage 21.1.2 HTTP 头部信息 XHR Head ...
- 在Windows8.1中通过IIS发布网站产生HTTP Error 503错误的解决方案
1.解决IIS下网站Bin目录中32位DLL不能使用,如图所示 2.解决通过IIS浏览网站,出现Http503的问题,如图所示
- Qt之绘制闪烁文本
简述 根据之前的二位绘图,我们可以很轻松的进行文本的绘制,如果需要一些特效,比如:文本闪烁.我们就必须借助其它辅助类来完成. 简述 原理 实现 效果 源码 原理 主要涉及两个辅助类: QFontMet ...
- bug:C#线程间操作无效: 从不是创建控件" XX" 的线程访问它
今天遇到这个问题,百度了下,把解决的方法总结出来.我们在ui线程创建的子线程操作ui控件时,系统提示错误详细信息为:线程间操作无效: 从不是创建控件“XXX”的线程访问它. 就我知道的有三种方法,先看 ...
- cf--------(div1)1A. Theatre Square
A. Theatre Square time limit per test 2 seconds memory limit per test 64 megabytes input standard in ...