(转载请注明原创于潘多拉盒子)

构造函数和析构函数是C++中再熟悉不过的概念了,几乎每个了解一点C++的人都知道这两个概念是什么意思。一个对象的全部生命期中构造函数和析构函数执行的时机如下:

1. 为对象分配空间。这个空间可能是在栈上(函数内的局部变量),可能是在数据区(静态变量、全局变量),也可能分配在堆上(new出来的变量)。

2. 执行对象对应的构造函数。如果继承有父类或有成员对象,则先执行父类的构造函数和成员对象的构造函数。

3. 对象生命期内的各种成员函数调用。

4. 执行析构函数。和#2中构造的过程相反,先执行自身的析构函数,再执行父类和成员对象的析构函数。

5. 释放为对象分配的空间。这个过程与#1相反。

对象的整个生命期中构造函数和析构函数的执行是具有非常精确的镜像对称性,也就是说,构造过程(包括构造函数和继承类、成员对象的构造函数)中的各个构造操作和析构过程(包括构造函数、成员对象、成员对象的析构函数)完全对称。这个过程在《C++对象模型》中有详细的说明。

对于#1,对象占有的空间,包括对象直接占有的空间,其大小也就是sizeof操作符给出的对象大小。C++要求对象的大小在编译时确定,因此该大小是在对象定义时确定下来的。

考虑如下的一段代码:

std::vector<int> u(10);
std::vector<int> v(20);
memcpy(&v, &u, sizeof(u));

首先第一个问题是:sizeof(u)和sizeof(v) 哪个更大?我见过有一部分C++程序员认为sizeof(v)会更大,原因是v里面存着20个int型变量,而v中存着10个int型变量。实际上,u和v的sizeof运算结果是一样大的!具体的大小可能跟编译器有关,但在一种编译器下,它们的大小是完全相等的!因为它们的类型是相同的!

由此也就知道了第3行代码是有问题的,将两个vector的对象按地址和大小拷贝,虽然本身可以编译并运行,甚至编译器也不会报告警告!但是会导致v持有u在构造函数中从堆上分配的空间,一方面导致double free,另一方面会导致v原先持有的堆上分配的空间泄漏!

有趣的是,可以利用构造函数和析构函数执行的时机,去利用编译器产生一些非常智能的代码。这里有一些典型的应用,比如std::auto_ptr:

std::auto_ptr<Widget> widget = new Widget();
// 像指针一样使用widget
widget->foo();
(*widget).bar();
if (widget->invalid())
{
return false; // 会在析构widget对象时自动释放new出来的Widget
}
// 即使有exception抛出,widget也会自动析构
return true; // 尤其是有多个return的时候,威力更大。

我们经常在实际中使用线程锁,在每个return前面都释放锁实在是一件麻烦的事情,而且也不是异常安全的,如果利用构造函数和析构函数,则能比较好的解决这个问题:

#include <pthread.h>

class ScopedLock
{
public:
ScopedLock(pthread_mutex_t& lock) : _lock(&lock)
{
pthread_mutex_lock(_lock);
} ~ScopedLock()
{
pthread_mutex_unlock(_lock);
} private:
  pthread_lock_t* _lock;
}; // 用例
int needForSafety()
{
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
ScopedLock scopedLock(&lock); // 加锁 // throw an exception:自动解锁
// return 1: 自动解锁
// return 2: 自动解锁
// ……
}

构造函数和析构函数的这种特性,在实际中是很有用的。

C++的优秀特性3:构造函数和析构函数的更多相关文章

  1. C++的优秀特性6:智能指针

    (转载请注明原创于潘多拉盒子) 智能指针(Smart Pointer)是C++非常重要的特性.考虑如下一段使用简单指针(Plain Pointer)的代码: A* a = new A(); B* b ...

  2. 不可或缺 Windows Native (21) - C++: 继承, 组合, 派生类的构造函数和析构函数, 基类与派生类的转换, 子对象的实例化, 基类成员的隐藏(派生类成员覆盖基类成员)

    [源码下载] 不可或缺 Windows Native (21) - C++: 继承, 组合, 派生类的构造函数和析构函数, 基类与派生类的转换, 子对象的实例化, 基类成员的隐藏(派生类成员覆盖基类成 ...

  3. C++C++中构造函数与析构函数的调用顺序

    http://blog.csdn.net/xw13106209/article/details/6899370 1.参考文献 参考1: C++继承中构造函数.析构函数调用顺序及虚函数的动态绑定 参考2 ...

  4. C++-理解构造函数、析构函数执行顺序

    先初始化序列中的函数调用,如果基类构造函数为非引用传递,则引起参数的拷贝构造 再: 先类内的成员构造函数(拷贝/默认),再类的构造函数:先基类,再派生类: 本文主要说明对象创建时构造函数的执行顺序,对 ...

  5. C++的优秀特性2:inline 函数

    (转载请注明原创于潘多拉盒子) Inline函数是C++的一个很小的特性,在不计较效率的情况下,这个特性似乎可有可无.然而,C++天生是为最为广泛的应用场景设计的,因此,总会有关于效率的问题.其实,除 ...

  6. 构造函数与析构函数(construction undergoing)

    构造函数和析构函数 一.构造函数: 1.普通构造函数:在对象被创建时利用特定的值构造对象,将对象初始化到一个特定的状态. 特性:构造函数的函数名和类名相同:没有返回值:在对象被创建时被自动调用:如果有 ...

  7. C++构造函数和析构函数,以及构造函数特殊成员变量和函数的初始化

    body, table{font-family: 微软雅黑; font-size: 10pt} table{border-collapse: collapse; border: solid gray; ...

  8. C++(1)C++类四个默认函数---构造函数、析构函数、拷贝函数、赋值函数

    C++构造函数和析构函数 默认构造函数指不带参数或者所有参数都有缺省值的构造函数!!! (1)构造函数.析构函数与赋值函数 构造函数.析构函数与赋值函数是每个类最基本的函数.它们太普通以致让人容易麻痹 ...

  9. 转 C++构造函数、析构函数、虚函数之间的关系

    C++构造函数.析构函数.虚函数之间的关系 1. 如果我们定义了一个构造函数,编译器就不会再为我们生成默认构造函数了.2. 编译器生成的析构函数是非虚的,除非是一个子类,其父类有个虚析构,此时的函数虚 ...

随机推荐

  1. 查一下 excel中某一列是否有重复

    另一列中写入 =IF(COUNTIF(C:C,C1)>1,"有重复","") 其余往下拖拉公式 我在想如果可以有不往下拖的呢? 不过好像筛选中也有类似的选 ...

  2. hdu 1565(状态压缩基础题)

    题意:容易理解. 分析:这是我做的状态压缩第二题,一开始超内存了,因为数组开大了,后来超时了,因为能够成立的状态就那么多,所以你应该先把它抽出来!!总的来说还是比较简单的!! 代码实现: #inclu ...

  3. C# 使用NPlot绘图技巧

    原文 C# 使用NPlot绘图技巧 图表控件一直是很难找的,特别是免费又强大的.NPlot是一款非常难得的.Net平台下的图表控件,能做各种曲线图,柱状图,饼图,散点图,股票图等,而且它免费又开源,使 ...

  4. JAVA CAS原理、unsafe、AQS

    concurrent包的实现 由于java的CAS同时具有 volatile 读和volatile写的内存语义,因此Java线程之间的通信现在有了下面四种方式: A线程写volatile变量,随后B线 ...

  5. hdu 4508 湫湫系列故事——减肥记I(完全背包)

    题意:完全背包 思路:完全背包 可以直接转化为 多重背包,num[i]=_v/c[i];//转为多重背包然后运用 多重背包 3种解法如下码1: #include<iostream> #in ...

  6. SqlServer 列的增加和删除

    有些时候我们需要删除或增加数据库中有数据中表的列.总结一下列的删除和增加. 1. 删除列 当表中存在数据时,删除列后,数据也会被删除. sql语句: alter table 表名 drop colum ...

  7. 【转】linux mknod命令解析

    转自:http://www.cnblogs.com/cobbliu/archive/2011/07/05/2389014.html 个人觉得linux的软件设计思想异常强大,比如把所有的设备都当做文件 ...

  8. android命名规范

    Android 开发规范 (陈杨) (一)注意事项 1. 编码方式统一用UTF-8. Android Studio默认已是UTF-8,只要不去改动它就可以了. 2. 缩进统一为4个空格,将Tab si ...

  9. OpenGl从零开始之坐标变换(下)

    这节主要来理解投影变换和视口变换的使用. 1.正射投影:glOrtho 函数原型: void glOrtho(GLdouble left,GLdouble right,GLdouble bottom, ...

  10. Android之操作SQLite

    一.SQLite的介绍 1.SQLite简介 SQLite是一款轻型的数据库,是遵守ACID的关联式数据库管理系统,它的设计目标是嵌入  式的,而且目前已经在很多嵌入式产品中使用了它,它占用资源非常的 ...