[转]C++基本功和 Design Pattern系列 ctor & dtor
今天Aear讲的是class.ctor 也就是constructor, 和 class.dtor, destructor. 相信大家都知道constructor 和 destructor是做什么用的,基本功能我就不废话了。下面先说效率的问题,让我们看个简单的例子:
class SomeClass; // forward declaration
class AnotherClass {
private:
SomeClass SomeClassInstance;
public:
AnotherClass(const SomeClass & Para) { SomeClassInstance = Para; };
~AnotherClass();
};
也许这是很多初学者经常写出来的代码,Aear以前也写过。让我们来看看这段代码有什么问题。
首先需要说明的是,在一个class实例化之前,所有的member都会被初始化,如果member是个class,那么那个class的 constructor就会被调用。也就是说,在运行AnotherClass的constructor之前,SomeClass的 constructor就已经运行了。接下来的代码里,SomeClassInstance又被重新执行了次 = 操作。也就是说,我们在给 SomeClassInstance附初值的时候,调用了2次SomeClass的method. 这个浪费也太大了,比较标准的方式是使用初始化列表,如下:
AnotherClass (const SomeClass & Para): SomeClassInstance(Para) {};
如果有多个类成员,可以用","来分割,如:
AnotherClass (const SomeClass & Para1, UINT32 Para2):
SomeClassInstance(Para1),
SecondAttr(Para2),
ThirdAttr(Para3) {};
值得注意的是, 类成员的初始化顺序和在类中的声明顺序应该一致。这个是有compiler来控制的,并不根据你在AnotherClass的 constructor中提供的初始化顺序来进行。所以,如果你想先初始化ThirdAttr,然后把ThirdAttr传到SecondAttr作为初始化参数,是会失败的。只有改变声明顺序才会成功。
同理,在声明类变量被附初值的时候,使用拷贝构造函数,效率更高:
=====错误=====
class x1;
x1 = x2;
=====正确=====
class x1(x2);
===================分割线===================
从上面的例子可以看到,几乎所有的class,都需要提供拷贝构造函数,也就是 className(const className &)。同时值得注意的是,如果提供了拷贝构造函数,一般也就需要提供 "="操作,也就是 className & operator = (const className &),说到 operator =, 也有必要强调下implicit type conversion的问题,这将会在以后的章节张有详细描述。至于为什么要提供 operator =,举个简单的例子:
class1 {
public:
class1() { p = new int[100]; };
~class1() { delete[] p; };
private:
char* p;
} x1, x2;
如果class1不提供operator =, 那么运行 x1 = x2的时候,C++会运行最基本的拷贝操作,也就是 x1.p = x2.p,那么在 x1被释放的时候,delete p;被执行。这时候 x2再要访问p,p已经变成非法指针了。 也许有人会说,我才不会用x1 = x2这么危险的操作,那让我们看看更加隐性的操作吧,例子如下:
void func(class1 Para) {...};
func(x1);
这时候,c++会调用class1的拷贝构造函数,来把参数从x1里拷贝到Para,如果class1没有提供copy constructor,那么c+ +就执行简单拷贝工作,也就是 Para.p = x1。当func返回的时候,Para被释放,调用 Para.~class1(),并且 delete p;那么x1.p就变成非法指针了。
这样大家就知道为什么要同时提供copy constructor和 operator =了吧。特别是在class里有指针的情况下,必须提供以上2个method。如果不想提供,可以把他们设为private,代码如下:
class1 {
...
private:
class1 (const class1 &);
class1 & operator = (const class1 &);
}
这样别人在执行 = 和 func()的时候就会报错了。
还有,在声明构造函数的时候,单参数的构造函数,最好都用explicit来声明,例如:
class1 {
public:
class1(int Para) {...}
...
};
其中class1(int Para)是个单参数的构造函数,如果执行下列操作,如:
class1 x1 = 2;
的时候,因为2不是class1,所以c++会用隐性的类型转换,也就是把2转换成class1,因此会调用class1(2),然后用operator = 符值给 x1. 这种操作经常会产生很多问题。比如如果我们提供了 operator == ,那么 在 if(x1 == 2)的时候,c++也会进行类似的操作,可能会产生我们不需要的结果。所以,对于这种单参数的constructor 最好做如下声明:
explicit class1 (int Para) {...}
这样做再执行 class1 x1 = 2;的时候就会报错了,explicit的意思就是C++ 的compiler不能做隐性类型转换,必须由程序员做type cast,比如:
class1 x1 = static_cast<class1>(2) 才会成功。
===================分割线===================
在运行constructor的时候,值得注意的一点就是,如果在constructor里,要初始化会throw exception的代码,一定要在constructor里catch。比如:
class1 {
class1()
{
pInt = new int[100];
try {
pClass2 = new pClass2;
}catch(...)
{ delete pInt; throw; };
}
}
大家看的明白了吧,如果不catch pClass2的exception,pInt分配的内存就不会释放,因为constructor如果失败,c++是不会调用destructor的。
===================分割线===================
最后关于destructor,需要注意的是,如果是被继承的base class,destructor一定要是virtual。比如:
BaseClass ()
{
public:
BaseClass();
virtual ~BaseClass();
}
DerivedClass : public BaseClass()
{
public:
DerivedClass();
~DerivedClass();
}
BaseClass * pBase = static_cast<BaseClass *>(new DerivedClass());
delete pBase;
如果BaseClass的destructor是virtual,那么正确的ctor dtor调用顺序是:
BaseClass();
DerivedClass();
~DerivedClass();
~BaseClass();
如果不是Virtual,调用顺序是:
BaseClass();
DerivedClass();
~BaseClass();
也就是说,DerivedClass的派生类不能被正确调用,这主要是因为在delete的时候c++并不知道你delete的是 DerivedClass, 因此需要把BaseClass的 dtor 设置成 virtual, 这样可以使用 vptr在 vtbl中查找 destructor,从而能够正确的调用destructor。
===================分割线===================
从上面的例子大家也看出来了,如果是派生类,那么就要调用基类的constructor,在多层次的派生类创建过程中,所以基类的constructor都要被调用。 destructor同理。因此要想提高效率,可以在关键代码短使用非派生类。
也许有人会说,所有的constructor和destructor都被compiler inline了,但是即使是inline并且 base class的constructor中不进行任何操作,c++也要为每个类设置vptr,也是有不需要的overhead。当然,我们得到效率的同时,失去的是可扩展性,良好的程序层次结构等等,大家要根据具体情况来权衡。
[转]C++基本功和 Design Pattern系列 ctor & dtor的更多相关文章
- 【转载】C++基本功和 Design Pattern系列 ctor & dtor
最近实在是太忙了,无工夫写呀.只能慢慢来了.呵呵,今天Aear讲的是class.ctor 也就是constructor, 和 class.dtor, destructor. 相信大家都知道const ...
- 设计模式(Design Pattern)系列之.NET专题
最近,不是特别忙,重新翻了下设计模式,特地在此记录一下.会不定期更新本系列专题文章. 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结. 使用 ...
- 说说设计模式~大话目录(Design Pattern)
回到占占推荐博客索引 设计模式(Design pattern)与其它知识不同,它没有华丽的外表,没有吸引人的工具去实现,它是一种心法,一种内功,如果你希望在软件开发领域有一种新的突破,一个质的飞越,那 ...
- [转]Design Pattern Interview Questions - Part 4
Bridge Pattern, Composite Pattern, Decorator Pattern, Facade Pattern, COR Pattern, Proxy Pattern, te ...
- [转]Design Pattern Interview Questions - Part 2
Interpeter , Iterator , Mediator , Memento and Observer design patterns. (I) what is Interpreter pat ...
- [转]Design Pattern Interview Questions - Part 3
State, Stratergy, Visitor Adapter and fly weight design pattern from interview perspective. (I) Can ...
- [转]Design Pattern Interview Questions - Part 1
Factory, Abstract factory, prototype pattern (B) What are design patterns? (A) Can you explain facto ...
- design pattern
1. visitor design pattern http://butunclebob.com/ArticleS.UncleBob.IuseVisitor
- Design Pattern: Observer Pattern
1. Brief 一直对Observer Pattern和Pub/Sub Pattern有所混淆,下面打算通过这两篇Blog来梳理这两种模式.若有纰漏请大家指正. 2. Use Case 首先我们来面 ...
随机推荐
- 开启SQL Server 2012的远程连接
有个远程的SQL Server服务器需要连接,因为SQL server默认是把远程连接关闭的. 以下有文档,照着做了,但是还是连不上, http://www.2cto.com/database/201 ...
- 14.5.2.3 Consistent Nonlocking Reads 一致性非锁定读
14.5.2.3 Consistent Nonlocking Reads 一致性非锁定读 一致性读意味着 InnoDB 使用多版本来实现一个查询数据库的快照在某个时间点. 查看看到的事务做出的改变被提 ...
- C Looooops(扩展欧几里得求模线性方程)
http://poj.org/problem?id=2115 题意:对于C的循环(for i = A; i != B; i+=C)问在k位存储系统内循环多少次结束: 若循环有限次能结束输出次数,否则输 ...
- bzoj1146
这是一道无比繁琐的题目话说这道题使我第一次练dfs序,比较感动:首先dfs序就是在dfs过程中按照访问的顺序给每个点标上两个“时间戳”一个是第一次访问到点i时的时间戳c[i],一个是访问完以i为根时的 ...
- 数学(矩阵乘法):HDU 4565 So Easy!
So Easy! Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Su ...
- 饭卡 (背包01 一维数组) http://acm.hdu.edu.cn/showproblem.php?pid=2546
/* 从一组数据中选出n个数,使这n个数的和最接近一个值x, 背包问题, 从一系列菜中,从最贵的菜(MAX)之外中选出几个菜,使菜的总价格sum最接近money-5:money-sum-MAX; 钱数 ...
- [Audio processing] 常见语音特征 —— LPC
共振峰产生的原理及其在音质上的体现,共振峰的分布位置是建立在声音产生媒介的共鸣物理结构基础上的(Resonant Physical Structure). 无论是人声还是乐器,它们的声音特性都源自 ...
- Flex3在应用RemoteObject出现问题解决方法
出现该问题 <mx:RemoteObject id="robj" destination="hello" endpoint="http://lo ...
- html特殊符号列表
特殊符号 命名实体 十进制编码 特殊符号 命名实体 十进制编码 Α Α Α Β Β Β Γ Γ Γ Δ Δ Δ Ε Ε Ε Ζ Ζ Ζ Η Η Η Θ Θ Θ Ι Ι Ι Κ Κ Κ Λ Λ Λ Μ ...
- Android(java)学习笔记246:ContentProvider使用之学习ContentProvider(内容提供者)的目的
1.使用ContentProvider,把应用程序私有的数据暴露给别的应用程序,让别的应用程序完成对自己私有的数据库数据的增删改查的操作. 2.ContentProvider的应用场景: 获取手机系统 ...