温故知新----再谈构造函数

温故知新

                   ----再谈构造函数

作者: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);    //可以改成这种形式

2001/9/13

丁宁 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值