1、浅拷贝和深拷贝
所谓浅拷贝就是由默认的拷贝构造函数所实现的对数据成员逐一赋值的方式。若类中含有指针类型的数据,这种方式只是简单地把指针指向赋值给新成员,但是并没有给新成员分配内存,因此这种方式必然会导致错误。为了解决浅拷贝出现地错误,必须显示地定义一个拷贝构造函数,使之不但能复制数据成员,而且为对象分配内存空间,这种方式就是深拷贝。
2、浅拷贝
浅拷贝也称位拷贝,即编译器只是将对象中地值采用基本类型值复制地方式拷贝过来,如果对象中管理资源,那么多个对象就会共享同一份资源,当释放内存时会使得同一份内存释放多次而导致错误。
【例】
#include<iostream>
#include<stdlib.h>
using namespace std;
class String
{
public:
//构造函数
String(const char* str)
:_str(new char[strlen(str) + 1])//分配动态内存
{
if(_str != NULL)
{
strcpy(_str, str);
}
}
//成员函数
char* cs_str()
{
return _str;
}
//析构函数
~String()
{
if(_str != NULL)
{
delete[] _str;
}
}
private:
char* _str;
};
void Test()
{
String s1("hello world!");
String s2(s1);
cout << s1.cs_str() << endl;
cout << s2.cs_str() << endl;
}
int main()
{
Test();
system("pause");
return 0;
}
运行结果:
分析:
1)程序运行创建s1时调用构造函数,用运算符new从堆上分配一块空间,并用s1指向这块内存空间。在执行s2语句时,因为没有定义拷贝构造函数,所以调用默认地拷贝构造函数,则s1和s2指向同一块内存空间。
2)主程序结束时,对象被逐个撤销,先撤销对象s2(因为s2后创建),撤销前先调用析构函数,用delete运算符释放所分配的内存空间。撤销对象s1时,第二次调用析构函数,因为这时_str所指向的内存空间已经被释放,企图对同一块内存空间释放两次,所以程序出错。过程如下图所示:
3、深拷贝
造成上述错误,如何解决?为了解决浅拷贝出现地错误,必须显示地定义一个拷贝构造函数,使之为对象分配内存空间,复制数据成员。
【例】传统写法
#include<iostream>
#include<stdlib.h>
using namespace std;
class String
{
public:
//构造函数
String(const char* str)
:_str(new char[strlen(str) + 1])
{
if(_str)
{
strcpy(_str, str);
}
}
//拷贝构造函数
String(const String& str)
:_str(new char[strlen(str._str) + 1])
{
if(_str)
{
strcpy(_str, str._str);
}
}
//赋值运算符重载
String& operator=(const String& str)
{
if(this != &str)
{
_str = new char[strlen(str._str) + 1];
if(_str)
{
strcpy(_str, str._str);
}
}
return *this;
}
//成员函数,返回成员变量
char* cs_str()
{
return _str;
}
//析构函数
~String()
{
if(_str)
{
delete[] _str;
}
}
private:
char* _str;
};
void Test()
{
String s1("hello world!"); //调用构造函数
String s2(s1); //调用拷贝构造函数
String s3 = "hello"; //调用构造函数
s3 = s1; //调用赋值运算符函数
cout << s1.cs_str() << endl;
cout << s2.cs_str() << endl;
cout << s3.cs_str() << endl;
}
int main()
{
Test();
system("pause");
return 0;
}
运行结果:
处理过程如下图所示:
【例】现代写法
#include<iostream>
#include<stdlib.h>
using namespace std;
class String
{
public:
//构造函数
String(const char* str)
:_str(new char[strlen(str) + 1])
{
if(_str)
{
strcpy(_str, str);
}
}
//拷贝构造函数
String(const String& str)
:_str(NULL)
{
String tmpstr(str._str);
swap(_str, tmpstr._str);//交换函数
}
//赋值运算符重载
String& operator=(const String& str)
{
if(this != &str)//避免自己给自己赋值
{
String tmpstr(str._str);
swap(_str, tmpstr._str);
}
return *this;
}
//成员函数,打印成员变量
char* cs_str()
{
return _str;
}
//析构函数
~String()
{
if(_str)
{
delete[] _str;
}
}
private:
char* _str;
};
void Test()
{
String s1("hello world!"); //调用构造函数
String s2(s1); //调用拷贝构造函数
String s3 = "hello"; //调用构造函数
s3 = s1; //赋值运算符函数
cout << s1.cs_str() << endl;
cout << s2.cs_str() << endl;
cout << s3.cs_str() << endl;
}
int main()
{
Test();
system("pause");
return 0;
}
运行结果:
赋值运算符重载的另外一种写法:
String& operator=(String str)
{
swap(_str, str._str);
return *this;
}