C++的优秀特性3:构造函数和析构函数
(转载请注明原创于潘多拉盒子)
构造函数和析构函数是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:构造函数和析构函数的更多相关文章
- C++的优秀特性6:智能指针
(转载请注明原创于潘多拉盒子) 智能指针(Smart Pointer)是C++非常重要的特性.考虑如下一段使用简单指针(Plain Pointer)的代码: A* a = new A(); B* b ...
- 不可或缺 Windows Native (21) - C++: 继承, 组合, 派生类的构造函数和析构函数, 基类与派生类的转换, 子对象的实例化, 基类成员的隐藏(派生类成员覆盖基类成员)
[源码下载] 不可或缺 Windows Native (21) - C++: 继承, 组合, 派生类的构造函数和析构函数, 基类与派生类的转换, 子对象的实例化, 基类成员的隐藏(派生类成员覆盖基类成 ...
- C++C++中构造函数与析构函数的调用顺序
http://blog.csdn.net/xw13106209/article/details/6899370 1.参考文献 参考1: C++继承中构造函数.析构函数调用顺序及虚函数的动态绑定 参考2 ...
- C++-理解构造函数、析构函数执行顺序
先初始化序列中的函数调用,如果基类构造函数为非引用传递,则引起参数的拷贝构造 再: 先类内的成员构造函数(拷贝/默认),再类的构造函数:先基类,再派生类: 本文主要说明对象创建时构造函数的执行顺序,对 ...
- C++的优秀特性2:inline 函数
(转载请注明原创于潘多拉盒子) Inline函数是C++的一个很小的特性,在不计较效率的情况下,这个特性似乎可有可无.然而,C++天生是为最为广泛的应用场景设计的,因此,总会有关于效率的问题.其实,除 ...
- 构造函数与析构函数(construction undergoing)
构造函数和析构函数 一.构造函数: 1.普通构造函数:在对象被创建时利用特定的值构造对象,将对象初始化到一个特定的状态. 特性:构造函数的函数名和类名相同:没有返回值:在对象被创建时被自动调用:如果有 ...
- C++构造函数和析构函数,以及构造函数特殊成员变量和函数的初始化
body, table{font-family: 微软雅黑; font-size: 10pt} table{border-collapse: collapse; border: solid gray; ...
- C++(1)C++类四个默认函数---构造函数、析构函数、拷贝函数、赋值函数
C++构造函数和析构函数 默认构造函数指不带参数或者所有参数都有缺省值的构造函数!!! (1)构造函数.析构函数与赋值函数 构造函数.析构函数与赋值函数是每个类最基本的函数.它们太普通以致让人容易麻痹 ...
- 转 C++构造函数、析构函数、虚函数之间的关系
C++构造函数.析构函数.虚函数之间的关系 1. 如果我们定义了一个构造函数,编译器就不会再为我们生成默认构造函数了.2. 编译器生成的析构函数是非虚的,除非是一个子类,其父类有个虚析构,此时的函数虚 ...
随机推荐
- (C#)RichTextBox控件
RichTextBox(有格式文本)控件可实现TextBox控件的所有功能. ❶在RichTextBox控件中显示滚动条 RichTextBox可设置Multiline属性来控制是否显示滚动套,tru ...
- 模式自由(Schema-free)和数据存储的非格式化趋势
最近遐想,数据存储的非格式化趋势. 格式化表格到自由的XML存储 数年以前,多家数据库厂商开始XML数据库存储.XML数据作为一种自描述的半结构化数据为Web的数据管理提供了新的数据模型,如果将XML ...
- selenium python (十五)控制滚动条操作
#!/usr/bin/python# -*- coding: utf-8 -*-__author__ = 'zuoanvip' #一般用到操作滚动条的两个场景 #注册时的法律条文的阅读,判断用户 ...
- Selenium2Library系列 keywords 之 _SelectElementKeywords 之 select_from_list_by_label(self, locator, *labels)
def select_from_list_by_label(self, locator, *labels): """Selects `*labels` from list ...
- ANSI
为了扩充ASCII编码,以用于显示本国的语言,不同的国家和地区制定了不同的编码标准,由此产生了GB2312.BIG5.JIS等各自的编码标准.这些使用两个字节来代表一个字符的各种汉字延伸编码方式被称为 ...
- C#开源框架(整理)
http://www.cnblogs.com/chinanetwind/p/3715809.html http://www.cnblogs.com/chinanetwind/p/3715813.htm ...
- C#轻量级企业事务 - TransactionScope
using System; using System.Data.SqlClient; using System.Transactions; namespace SomeDBTransaction { ...
- Select模型及tcp select模型
参考:http://m.blog.csdn.net/article/details?id=51420015 一.套接字模式 套接字模式简单的决定了操作套接字时,Winsock函数是如何运转的.Wins ...
- wifi reaver
PIN码的格式很简单, 八位十进制数,最后一位(第8位)为校验位(可根据前7位算出),验证时先检测前4位,如果一致则反馈一个信息,所以只需1万次就可完全扫描一遍前4位,前4位确定下来的话,只需再试10 ...
- Yii 1.1 URL两个笔记 同时支持PATH于GET路由和隐藏index.php
同时支持PATH于GET格式路由(修改框架文件 简直坑) framework/web/CUrlManager.php parseUrl方法 第一行判断修改成 if($this->getUrlFo ...