现在的位置: 首页 > 综合 > 正文

拷贝构造函数的作用

2013年10月20日 ⁄ 综合 ⁄ 共 4091字 ⁄ 字号 评论关闭

 

http://www.programfan.com/club/showtxt.asp?id=175861

拷贝构造函数的作用
                                                -----小峰
    如果你没有为你的程序添加一个拷贝构造函数,那么编译器也会向你的程序添加一个默认的拷贝构造函数.在一般情况下使用编译器的默认拷贝构造函数就可以了,但是如果你的类的成员数据有指针变量的存在,那么你最好自己构造一个拷贝构造函数,否则你的程序可能隐藏着许多错误.
请看下面这一个程序,存在多少错误?

#include "stdafx.h"
#include "iostream.h"
#include "string.h"
class String
{
int nLen;
char *cArray;
public:
String(){nLen=0; cArray=NULL;}
~String(){delete []cArray; nLen=0;}
String(const char *str)
{
int len=strlen(str)+1;
cArray=new char[len];
strcpy(cArray,str);
nLen=len;
}
void operator=(const char *str)
{
if(nLen)
delete []cArray;
int len=strlen(str)+1;
cArray=new char[len];
strcpy(cArray,str);
nLen=len;
}
char *GetBuffer()const{return cArray;}
//----------------------------------------------------
String  operator=(String p)
{
if(nLen)
delete []cArray;
strcpy(p.cArray,p.GetBuffer());
int len=strlen(p.GetBuffer())+1;
cArray=new char[len];
nLen=len;
strcpy(cArray,p.GetBuffer());
return *this;
}
//---------------------------------------------------
};

int main(int argc, char* argv[])
{
String str("meihaofeng");
char *p=str.GetBuffer();
cout<<p<<endl;
str="mei";
p=str.GetBuffer();
cout<<p<<endl;
String s="hello!";
str=s;//这里开始出错
p=str.GetBuffer();
cout<<p<<endl;
return 0;
}
初看也许你会觉得没有什么错误啊,但你将这段完整的程序拿去运行一下(编译链接是能通过的),啊!那些错误有些惊人.下面让我们来分析一下,出现那些错误的原因.
加粗的那个函数是出错的根本,主要就是针对这个函数进行分析.在主函数是从“str=s;”这一句开始出错的。执行str=s对应String  operator=(String p)函数上。首先将“s”作为参数拷贝给String  operator=(String p)的形参p。在这个过程中,s的两个成员数据
nLen和cArray也相应的拷贝给p的nLen和cArray.但是cArray是一个指针,拷贝之后p.cArray的值和s.cArray的值是相等的,可以知道这两个指针变量都指向同一片内存区域,这一个内存区域就是存储“hello!”的内存区域。当String  operator=(String p)这个函数执行完后,由于对象p超出了它的作用范围,所以调用对象p的析构函数,在析构函数中释放cArray所指向的内存,此时释放cArray所指向的的内存相当于释放p.cArray或主函数中s.cArray所指向的内在区域。释放完后返回*this对象,注意,返回的*this这个对象将拷贝给主函数的一个临时的对象,之后就释放这个临时对象的内存。在将*this拷贝给一个临时对象的过程如同将s作为实参传给形参p一样,使临时对象的两个数据成员nLen和cArray与(*this)对象的nLen和cArray相等,同理(*this).cArray与临时对象的cArray指向现一片内存区域。在释放这个临时对象时先调用这它的析构函数,从上面的程序可以知道这个*this代表主函数的一个对象str,所以在析构函数中就释放str.cArray所指向的内存。 
    从String  operator=(String p)中由前面的分析可知,主函数中s.cArray以及str.cArray所指向的内存已经被释放了。返回到主函数通过p=str.GetBuffer();cout<<p<<endl;打印str.cArray的值也就打印出了错误的值。当主函数中str与s两个对象的生存周期结束后,这两个对象的析构函数会被再次调用。在调用析构函数时,又会释放s.cArray和str.cArray所指向的内存,由于在String  operator=(String p)函数中这两个指针所指向的内存区域已经被释放了,所以在主函数中再次释放就会出错。这些就是程序中隐藏着的错误。

    最好的解决方法就是不要编译器添加的拷贝构造函数,而是自己手动添加一个,并在这个拷贝构造函数中执行一此操作。下面的程序修补了上面程序的错误,程序如下:
#include "stdafx.h"
#include "iostream.h"
#include "string.h"

class String
{  
int nLen;
char *cArray;
public:
String(){nLen=0; cArray=NULL;}
~String()
{
cout<<"~String()"<<endl;
delete []cArray;
nLen=0;
}
String(const char *str)
{
int len=strlen(str)+1;
cArray=new char[len];
strcpy(cArray,str);
nLen=len;
}
void operator=(const char *str)
{
if(nLen)
delete []cArray;
int len=strlen(str)+1;
cArray=new char[len];
strcpy(cArray,str);
nLen=len;
}
char *GetBuffer()const{return cArray;}
//----------------------------------------------------
String(const String & s)//自己添加的拷贝构造函数。
{
        cout<<"拷贝构造函数"<<endl;
        char *temp;
        temp=new char[s.nLen];
        strcpy(temp,s.GetBuffer());
        cArray=new char[s.nLen];
        strcpy(cArray,temp);
        delete temp;
}
//----------------------------------------------------
String  operator=(String p)
{
if(nLen)
delete []cArray;
strcpy(p.cArray,p.GetBuffer());
int len=strlen(p.GetBuffer())+1;
cArray=new char[len];
nLen=len;
strcpy(cArray,p.GetBuffer());
return *this;
}
//---------------------------------------------------
};

int main(int argc, char* argv[])
{
String str("meihaofeng");
char *p=str.GetBuffer();
cout<<p<<endl;
str="mei";
p=str.GetBuffer();
cout<<p<<endl;
String s="hello!";
str=s;
p=str.GetBuffer();
cout<<p<<endl;
return 0;
}
    在拷贝构造函数中要为目标对象中的指针分配空间,并让指针指向分配的空间。并将源对象的指针所指向的空间里的值复制到分配的空间中。注意这里的目标对象,对应到上面的程序“str=s”中,str即为目标对象,s为源对象。在String  operator=(String p)返回时临时对象为目标对象,*this为源对象。
下面是程序的运行结果:
---------------------------------
meihaofeng
mei
拷贝构造函数
拷贝构造函数
~String()
~String()
hello!
~String()
~String()
---------------------------------
从运行结果为可以看到拷贝构造函数被调用了两次,第一次是,执行str=s时,将实参s拷贝给String  operator=(String p)函数的形参p.
第二次是,在String  operator=(String p)返回里,将*this拷贝给一个临时对象。析构函数被调用了四次,从程序中也不难分析出是那四次.
第一、二是String  operator=(String p)中发生的。第三、四次是在主函数发生的。

 

抱歉!评论已关闭.