日期:2009-06-02  浏览次数:20541 次

由始至终

                         ----构造与析构


作者:HolyFire

我们在平时的生活中一般会总结出一些规律,早上起床会刷牙洗脸,晚上会洗澡睡觉,这些都成了惯例。使用瓶装调味品时先将瓶盖打开,用完后将瓶盖盖上。这是一种好习惯。但是有些人不同,他们往往偷懒,一个常常不刷牙不洗脸不洗澡的人会有体味,东西放得乱七八糟的人生房间很不整洁。这些都是我们不希望看到的。当然编程中我们也不希望代码乱七八糟。

使用一个未初始化的变量简直就是灾难,使用一个未初始化的指针将导致崩溃。这是我的忠告。在C++中初始化不会有附加的效果,不会降低效率,我们要做的是养成好习惯,产生一个对象的时候就将它初始化。

对于

Object.Init();

Object.Free();

这样的调用并不是很困难,要记住他也不是难事,但是谁都不能保证他永远不会忘记,更糟糕的是

Object.Init();

Object.Free();

没有配对使用

Object.Init();

Object.Free();

Object.Free();



Object.Init();

Object.Init();

Object.Free();

会带来什么样的结果,谁也不知道,而且这样的错误,编译器不会报错。这是多么可怕的错误,一个程序员最怕遇上的就是这样的逻辑错误,它可能为了找这样的一个错误花上一整天时间。

让我们看看有什么好的办法。

一个对象按时间来分析,一般有三个阶段,出生,活动,死亡。与我们要做的有什么相关之处呢,初始化,运行,释放。很好,对照一下,我们发现在对象出生的时候初始化,死亡的时候释放,如果这一切能用这样的机制来操作,我们就再也不用担心会由于忘记或错误的使用带来麻烦了。

C++里就提供了这样的机制。使用他有个约定

class Object{

public:

       Object();   //与类同名的函数,该函数没有返回值,叫做构造函数

~Object();  //类似的,在构造函数名前加一个取反符号,叫做析构函数

};

构造函数将在对象产生的时候调用

析构函数将在对象销毁的时候调用

调用的过程和实现方法由编译器完成,我们只要记住他们调用的时间就行了,而且他们的调用是自动完成的,不需要我们控制。

#include <iostream>

using namespace std;

class Object{

public:

       Object(){ cout << "Object ON!" << endl; }

       ~Object(){ cout << "Object OFF!" << endl; }

};

void main()

{

       Object o;

}

运行结果

Object ON!

Object OFF!

构在函数和析构函数确实的执行了

现在我们来一个应用的例子

一个字符串类,它需要保存字符串的内容,但是它不知道字符串的大小,那么设计这个字符串类的时候,保存字符串的成员变量就不能用固定大小的数组,而是用可以间接操作数组的指针。

#include <iostream>

#include <string.h>

using namespace std;

class string{

private:

       char * data;

public:

       string(){ data = NULL; }

       string( char * str )

              {

              cout << "Copy string: " << str << endl;

              data = new char[ strlen(str) + 1 ];

              memcpy( data , str , strlen(str) + 1 );

              }

       char * Data(){ return data; }

       ~string()

              {

              if( data )

                     {

                     cout << "Free string: " << data <<