拷贝构造函数的作用:1.传值;2.传返回值; 3.对象初始化赋值。
当从一个“老对象”复制出一个“新对象”时,新对象将产生自己的内存空间,并且新对象具有和老对象一样的内存空间结构;老对象的变量(指针、数字、字符、字符串等定长类型)值都会被拷贝入新对象中。
但是,如果老对象中有堆内存,在新对象中并不会复制堆内存,而是指向老对象的堆内存。这就是浅拷贝,C++类会默认生成浅拷贝。
浅拷贝的局限性是:新老对象指向相同的堆内存空间,一个对象的堆内存发生改变,另外一个也随之改变。当对象析构时,这块堆内存将释放两次。
如果我们想将老对象的堆内存也拷贝到新对象,就必须构建自己的拷贝构造函数,在拷贝构造函数中为新对象开辟堆内存、赋值,这就是深拷贝。
下面的例子用最“热门”的String为例:
浅拷贝的实现很简单:
m_data=another.m_data; //直接赋值,指针指向相同的堆内存
深拷贝:
m_data = new char[strlen(another.m_data) + 1]; //先开辟空间
strcpy(m_data,another.m_data); //再赋值
#include <iostream.h>
class String
{
public:
String(const char *str = NULL); // 默认构造函数
String(const String &another); // 拷贝构造函数
String& operator=(const String &rhs); // 赋值函数
~String(); // 析构函数
//注释掉private,便于调测
//private:
char *m_data; // 用于保存字符串
};
String::String(const char *str)
{
cout<<"String(const char *str)"<<endl;
if ( str == NULL ) //strlen在参数为NULL时会抛异常才会有这步判断
{
m_data = new char[1] ;
m_data[0] = '\0' ;
}
else
{
m_data = new char[strlen(str) + 1];
strcpy(m_data,str);
}
}
/*
//浅拷贝
String::String(const String &another)
{
cout<<"Shallow String(const String &another)"<<endl;
m_data=another.m_data; //浅拷贝。
}
*/
//深拷贝
String::String(const String &another)
{
cout<<"Deep String(const String &another)"<<endl;
m_data = new char[strlen(another.m_data) + 1];
strcpy(m_data,another.m_data);
}
//赋值函数用于重新赋值(之前已经有值(有内存空间)),拷贝构造函数用于初始化赋值(如果初始化时用“=”也会直接调用拷贝构造函数)。
//赋值函数就是用一个己经构造好的对象(rhs)来初始化另一个己经构造好的对象(this),这块内存区域(this->m_data)之前就己经开辟好了。(先释放原有的内存资源delete [] m_data, 这个动作,是为了防止内存泄漏;而拷贝构造函数就不需要,因为他是新开辟的内存区域,无需释放.
String& String::operator =(const String &rhs)
{
cout<<"operator =(const String &rhs)"<<endl;
if ( this == &rhs)
return *this ;
delete []m_data; //删除原来的数据,新开一块内存
m_data = new char[strlen(rhs.m_data) + 1];
strcpy(m_data,rhs.m_data);
return *this ;
}
String::~String()
{
delete []m_data ;
}
int main()
{
String testStr = "hello, world!"; //调用默认构造函数
String Str2(testStr); //调用拷贝构造函数
cout<<(Str2.m_data)<<endl;
//如果这里调用的是浅拷贝,Str2.m_data指向的值会随着testStr.m_data指向的值改变而改变。
*(testStr.m_data) = 'x';
cout<<(Str2.m_data)<<endl;
String Str3;//调用默认构造函数
Str3 = Str2;//调用赋值函数
return 1;
}