【转载】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。当然,我们得到效率的同时,失去的是可扩展性,良好的程序层次结构等等,大家要根据具体情况来权衡。

时间: 2024-09-30 00:26:03

【转载】C++基本功和 Design Pattern系列 ctor & dtor的相关文章

[转]C++基本功和 Design Pattern系列 ctor &amp; dtor

今天Aear讲的是class.ctor 也就是constructor, 和  class.dtor, destructor. 相信大家都知道constructor 和 destructor是做什么用的,基本功能我就不废话了.下面先说效率的问题,让我们看个简单的例子: class SomeClass;   // forward declaration class AnotherClass {private:    SomeClass SomeClassInstance;public:    Ano

Head First 之 Design Pattern(二):Observer Pattern

观察者模式是最常用的设计模式之一,[对象之间多对一的依赖关系,当一个对象发生变化时,其会通知所有依赖它的对象].拿订阅报纸和发行报社打比方,报社采集到news制作新的报纸,派送给订阅的客户,以此把最新的消息告知客户.所以,出版社 + 订阅者 = 观察者模式. 这种一对多的关系,也即"一个"主题."多个"观察者能够使得观察者仅仅了解主题推送的消息但不知晓其中的细节,而主题握有观察者列表但不干涉到观察者的个人隐私.所以,它们之间相互有交互,但不紧密,不清楚对方的细节.改

Design Pattern: Observer Pattern

1. Brief 一直对Observer Pattern和Pub/Sub Pattern有所混淆,下面打算通过这两篇Blog来梳理这两种模式.若有纰漏请大家指正. 2. Use Case 首先我们来面对一个老到跌渣的故事,并以从未听说过Observer Pattern为前提. 假设要设计一个新闻订阅系统,新闻分为商业.体育和八卦3种,而查收终端有PC.移动终端等,后续还不断增加新闻种类和查收终端. 需求如上,下面我们根据OOD的方式来构建概念模型. 新闻 <- 分类新闻 终端 <- 分类终端

Head First 之 Design Pattern(一):Strategy Pattern &amp;&amp; 初入设计模式殿堂

头一回阅读<Head First 设计模式>,感觉语言生动形象,把一个个抽象的模式描述的浅显易懂,就像白话文一样. 正如作者推荐如何使用这本书. 你懂Java吗(不太懂... 会C++不影响吧)? 你想学习.了解.记得并应用设计模式,以及其所基于的OO设计原则吗(是的,我正在深入学习C++呢)? 你是不是更喜欢一种轻松愉悦的学习环境(absolutely)? 这本书正是我需要的~   "Head First"就是那些不断学习的人们,像他们致敬,共勉加油~ 使用设计模式最好的

C# 版本设计模式(Design Pattern)(转)

C# 版本设计模式(Design Pattern)---------------总序 今天在写代码的时候,突然想到了一个非常有趣的问题,就是非常经典的一个问题:猫叫了,老师跑了,主人醒了. 也不知道自己脑子里面在想些什么,怎么突然蹦出来了这个想法,后来想想,是时候该谢谢关于设计模式的博客了,虽然设计模式对于程序猿来说应该不是一个陌生的技术. 设计模式主要是教大家如何写代码,更确切的说法应该是怎么组织代码,使代码更加优雅.使程序编码成为一门艺术. 那如何去理解“组织代码”呢?这本身就是一个非常有趣

DP什么意思 design pattern 设计模式

DP  design pattern 大话设计模式  中的DP 是设计模式的意思 设计模式的书 ,最经典最原始的就是 GOF 的<设计模式>了. 设计模式的书基本上大多是以这 20 多个模式分开讲.含<大话设计模式> 学了 OOL 写的程序基本上是 OB 的. 只有慢慢掌握了 DP 才能写出真正的 OO 程序. 思想 -> 设计原则 -> DP -> OOD

Head First Design Pattern 读书笔记(2) 观察者模式

Head First Design Pattern 读书笔记(2) Observer Pattern 观察者模式 Observer Pattern 类图 定义 观察者模式:在对象间定义一个一对多的关系,当其中一个的对象发生改变时,所有依赖于这个对象的对象(即观察者们)都会自动更新或做执行某些行为. 几个OO的原测 尽量以松耦合的方式处理对象间关系–>软件工程时候学的"高內聚,低耦合"的好处 关于观察者模式 被观察对象通知观察者可以使用推送的方式(类图中带参数的notifyActi

Design Pattern Singleton 单一模式

单一模式的几个注意点: 一) 设计单一模式,首先需要把构造函数给私有化了,不让外界访问,那么外界只能通过提供的函数获取一个新的类. 二) C++的单一模式,记得要在类外初始化一个类,否则或内存出错的. 三) 这个唯一的类必须是要静态的 程序: #ifndef _SINGLETON_H #define _SINGLETON_H #include <iostream> #include <string> using namespace std; class DuGuJiuJian {

Design Pattern 设计模式1 - Strategy 1

实现 : Defferent Heros attack Defferently. - 不同的英雄使用不用的招数 Strategy设计的思路: 基类A,更加小的基类B,新的继承类C: 1 从基类A中抽出一个更加小的基类B 2 利用这个更加小的基类B实现不同的效果 3 把这个更加小的基类B包含进基类A中去 4 新的继承类C只需要和基类A打交道,设计不同行为,不需要理会更加小的基类B #pragma once #ifndef _STRATEGY_HEROS_H #define _STRATEGY_HE