温故知新

发布时间: 2007-05-21 09:44    作者: 未知    来源: 未知    浏览:    评论

温故知新

                   ----再谈构造函数

作者:HolyFire

如果不知道构造函数的请先看一下《由始至终----构造与析构》,看过的我就不再多言,直接转入话题。

定义一个类的的实例的时候,可以看到这样的形式

classA a;        //构造函数不需要参数

不需要参数的构造函数称之为缺省构造函数。

不需要参数有两种情况

1:构造函数没有参数

2:构造函数有参数但可以不给出

class A{

public:

       A();          //构造函数没有参数

       A( int I = 10 );  //构造函数的参数有缺省值,可以不用给出

};

这两种情况都是缺省构造函数,但是由于缺省构造函数的特殊性(他是被自动调用的),编译器无法判断需要调用那一个,所以规定缺省构造函数只能有一个。

缺省构造函数的出现,意味着一个类型可以不依赖约束条件而被创建,就象一些细小的单元,质子,中子和电子,他们的有很大的类似性,不需要用条件来分辨他们被创建的信息。当然不需要用条件来分辨他们被创建的信息也包含了第二种情况,从流水线上生产的统一品种的产品很多都是用同一种方式的,那么创建他们的信息基本一致,也就是所符合第二种情况,参数可以采用缺省值。

这个例子我们可以举一个例子,我们创建一个指针类的时候,常常把他指向的内容置为空值,这很容易理解,我们需要一个指针,但是现在还不知道指向谁,等到我们要使用它的时候,不一定是知道他是否指向过别的对象,为了简化问题,一开始就将他置空,但是有时候我们需要用参数在创建的时候就给出指向的对象,特别是在产生临时对象的时候尤为管用,那么,我们使用一个参数缺省值为空的缺省构造函数。

classA a( a1 );    //构造函数有参数,而参数为一个相同的类型

这样的构造函数叫做拷贝构造函数,意思就是将类一个实例的内容复制到新创建的实例中去,为什么要这么做呢。我们来研究一下。

我们平时使用基本类型的时候,可以使用赋值语句,将相同类型的某个对象的内容赋给另一个对象

int a = 3;

int b;

b = a;     //这样的话,b中就有和a一样的内容了

还可在允许的情况下使用不同类型的赋值

int a = 3;

long b;

b = a;    //这样的话,b也能包含有和a一样的内容了

我们在设计类的时候应该也是将一个类作为一个个体,一个类型来处理,而且在现实中这样的行为也是存在的,一个人的个人资料可以写在不同的纪录簿上,一个软件可以拷贝好几份。

所以在面向对象编程中,这个问题不容忽视。

回到基本类型上,基本类型的处理编译器完成了,在C++中很简单,基本类型占用存储空间是连续的,所以不管原来的内容是什么,只要照搬照抄就可以了,这种负值方式叫做逐位拷贝,简称位拷贝。

int a = 3;

int b;

假设:对象在内存中的存储顺序是先高后低,每个内存单元为1字节(BYTE)=8位(BIT)

//假设这是a(int)的存储空间

0
3


//假设这是b(int)的存储空间

?
?


b =a ;

//将a的内容拷贝到b中

0
3


| |   | |

?
?


//a

0
3


//b

0
3


我们设计的类在内存中也是连续的,使用这样的拷贝方法会得到一个一模一样的同类型实例。而且编译器我们处理了这一件事(C++的编译器真好,它能解决的事,就不用麻烦我们了),也就是说即使我们没有定义拷贝构造函数,编译器也会在需要使用的时候,自己产生一个拷贝构造函数,使用的方法就是位拷贝。但是这样好吗,使用这种方法产生的新类可以安全的工作吗,应该有不少朋友已经产生了疑问。

什么时候可以让编译器自己处理拷贝构造函数。

#include <iostream>

using namespace std;

class A{

private:

        int x;

        int y;

        int z;

public:

        A():x(0),y(0),z(0){ }

        A( int _x = 0 , int _y = 0 , int _z = 0 ):x(_x),y(_y),z(_z){ }

        friend ostream& operator <<( ostream& , A const& );

};

ostream& operator <<( ostream& out , A const& arg )

{

        out << "This is a Instance of A" << endl;

        out << "Member Data x is : " << arg.x << endl;

        out << "Member Data y is : " << arg.y << endl;

        out << "Member Data z is : " << arg.z << endl;

        return out;

}

void main()

{

        A a( 1 , 12 ,123 );

        A b(a);

        cout << "This is a!" << endl;

        cout << a << endl;

        cout << "b is a copy of a!" << endl;

        cout << b;

}

结果是:

This is a!

This is a Instance of A

Member Data x is : 1

Member Data y is : 12

Member Data z is : 123

b is a copy of a!

This is a Instance of A

Member Data x is : 1

Member Data y is : 12

Member Data z is : 123

可以看出,位拷贝得出的结果是正确的。

上面的例子中成员变量都是在编译期间决定的,在内存中的位置也相对固定,如果成员变量的内容是在运行期间决定的呢,比如字符串成员变量,他需要在堆中动态分配内存。还能正常工作吗,继续看例子。

#include <iostream>

#include <string.h>

#include <mem.h>

using namespace std;

class A{

private:

       char * data;

public:

       A():data(NULL){ }

       A( char * _data ):data(NULL)

              {

              if( !_data )

                     return;

              int length = strlen(_data) +1;

              data = new char[length];

              memcpy( data , _data , length );

              }

       ~A()

              {

              if( data )

                     delete data;

              }

       void Clear( void )

              {

              if( data )

                     {

                     memset( data , 0 , strlen( data ) );

                     delete data;

                     }

              data = NULL;

              }



       friend ostream& operator <<( ostream& , A const& );

};

ostream& operator <<( ostream& out , A const& arg )

{

       out << "This is a Instance of A" << endl;

       if( arg.data && *arg.data )

              out << "Member Data data is : " << arg.data << endl;

       else

              out << "Member Data data is : NULL" << endl;

       return out;

}

void main()

{

       A a( "abcdefg" );

       A b(a);

       cout << "This is a!" << endl;

       cout << a << endl;

       cout << "b is a copy of a!" << endl;

       cout << b << endl;

       a.Clear();

       cout << "Where a's mem clear!" << endl;

       cout << a;

       cout << "God! b's mem clear!" << endl;

       cout << b << endl;

}

结果是:

This is a!

This is a Instance of A

Member Data data is : abcdefg

b is a copy of a!

This is a Instance of A

Member Data data is : abcdefg

Where a's mem clear!

This is a Instance of A

Member Data data is : NULL

God! b's mem clear!

This is a Instance of A

Member Data data is : NULL    //不!a中释放了内存连带着b的一起释放掉了。

这是当然的由于位拷贝,b中的data只是将a中的data复制过来了而已,并没有分配内存,拷贝字符串的内容。显而易见,使用位拷贝不能满足我们的要求,原来只需要简单的将成员变量的值简单的复制,这种我们称之为:浅拷贝。现在我们需要处理对应成员变量,用其他方法来得到我们需要的结果,这种我们称之为:深拷贝。

这样我们就需要自己写拷贝构造函数来实现深拷贝了。

#include <iostream.h>

#include <string.h>

#include <mem.h>

class A{

private:

       char * data;

public:

       A():data(NULL){ }

       A( char * _data ):data(NULL)

              {

              if( !_data )

                     return;

              int length = strlen(_data) +1;

              data = new char[length];

              memcpy( data , _data , length );

              }

       A( A const& arg )

              {

              if( !arg.data )

                     return;

              int length = strlen(arg.data) +1;

              data = new char[length];

              memcpy( data , arg.data , length );

              }

       ~A()

              {

              if( data )

                     delete data;

              }

       void Clear( void )

              {

              if( data )

                     {

                      memset( data , 0 , strlen( data ) );

                     delete data;

                     }

              data = NULL;

              }

       friend ostream& operator <<( ostream& , A const& );

};

ostream& operator <<( ostream& out , A const& arg )

{

       out << "This is a Instance of A" << endl;

       if( arg.data && *arg.data )

              out << "Member Data data is : " << arg.data << endl;

       else

              out << "Member Data data is : NULL" << endl;

       return out;

}

void main()

{

       A a( "abcdefg" );

       A b(a);

       cout << "This is a!" << endl;

       cout << a << endl;

       cout << "b is a copy of a!" << endl;

       cout << b << endl;

       a.Clear();

       cout << "Where a's mem clear!" << endl;

       cout << a;

       cout << "Good! b's mem not clear!" << endl;

       cout << b << endl;

}

结果是:

This is a!

This is a Instance of A

Member Data data is : abcdefg

b is a copy of a!

This is a Instance of A

Member Data data is : abcdefg

Where a's mem clear!

This is a Instance of A

Member Data data is : NULL

Good! b's mem not clear!

This is a Instance of A

Member Data data is : abcdefg   //哈哈,这正是我想得到的结果。

如果能使用位拷贝,尽量让编译器自己用位拷贝的方式处理,这样会提高效率。但是一定要谨慎,不然会产生不可预料的结果,如果你的类中有一个成员变量也是类,它使用了深拷贝,那么你也一定要使用深拷贝。

另外,我在《白马非马----继承》中说到,一个类型的的派生类是该类型的一种。那么。

class A;

class B: public A{

};

B b;

A a(b);

这样的形式是正确的。事实上,b先切片退化成一个临时变量tempb,类型是class A,有关A的部分原封不动的保留下来,然后使用A a(tempb)这样的方式成功的调用了。

拷贝构造函数并非可有可无!不能用其他函数来替代

看这样的例子

void function( A a);

在函数调用的时候按值传递参数,那么将在栈里产生一个class A的临时变量,如果没有拷贝构造函数,这个过程就无法自动完成,如果没用设计好浅拷贝或深拷贝,那么可能得不到正确结果。如果拷贝构造函数正确,那么我们可以轻松的获得我们想要的结果----按值传递的参数在函数执行后不受影响。

classA a = a1;    //拷贝构造函数

事实上就是这样的形式。

ClassA a(a1);    //可以改成这种形式

TAG

Smile Big Smile Surprise Stick out tongue Wink Sad Tongue Tied Indifferent Crying Embarrassed Cool Angry Angel Devil [8-|] [:#] [:-*] [:^)] [<:o)] [|-)] Yes Beer Left Hug Music Star Time Snail Pizza Automobile Umbrella Computer Storm [mo] [8o|] [^o)] [+o(] [*-)] [8-)] Coffee No Drinks [Z] Right Hug Cake Broken Heart Gift Wilted Flower Movie Dog Idea Sleep Email Travel Paradise
呢称:

加粗 斜体 下划线 链接 图片 代码 邮件地址 引用 列表

最多只能输入100个字符

Tags

SQL 数据库 asp.net C# XML 控件 .NET教程 程序 事件 数据 安全 代码 Server 客户端 验证 数据库专栏 接口 文件 Oracle DataSet 函数 DataGrid 问题 .net return C#语言 JavaScript 服务 IIS 对象 语句 windows 继承 时间 web.config 设计 开发 参数 变量 解决 字符 ADO.net 环境 VB.Net语言 web 异常 工具 服务器 计算 实例 OLEDB Application VB Word WebService insert asp net 安装 记录

精华推荐

更多

精品下载

更多