条款11:为需要动态分配内存的类(含有指针的类)声明一个拷贝构造函数呃一个赋值操作符
请注意下面这段代码:
#include <iostream>
class String
{
public:
String(const char* value)
{
if (value)
{
int length = strlen(value) + 1;
data = new char[length];
memcpy(data, value, length);
}
else
{
data = new char[1];
data[0] = '\0';
}
}
~String()
{
if (data)
{
delete[] data;
}
}
private:
char* data;
};
int main()
{
String a("Hello");
String b("World!");
b = a; //默认operator=
return 0;
}
以上这段代码会产生如下问题:
1.b.data和a.data指向了同一块内存,由于程序结束时,对象a和对象b都会调用各自的析构函数,导致一块相同的内存被释放了两次;
2.对象b.data指向的内存不会被删除,产生了内存泄露(注:程序关闭时,内存会被系统回收)。
之所以产生上述问题,是因为调用了默认的operator=,该操作是对类的数据成员进行逐位拷贝(为了兼容C语言的struct),所以对指针来说,得到的是地址,而并非里面的内容。
默认的拷贝构造函数和默认的赋值运算符几乎会产生一样的问题。
请看下面一个关于默认的拷贝构造函数的例子:
void doNothing(String localString) {}
String s = "The Truth Is Out There";
doNothing(s);
当s作为实参传递到函数中时,调用的是默认的拷贝构造函数,当形参离开他的生存空间,就会释放s所占用的内存,然后s在离开它自己的生存空间时,就会报错,因为对象s的析构函数去delete一个已经delete的指针。
所以,为了避免此类情况,只要类里面有指针,我们就要写自己版本的拷贝构造函数和赋值运算符函数。
有时候,我们要禁止类的赋值运算,我们可以把自定义的operator=函数声明为私有函数,并且不去定义它,这样一来,程序只要一有该类的赋值运算,编译器就会有反应(注:如果你去实现了该函数,则该类的成员函数和类的友元一样可能会使用该函数)。