如果一个类既没生命拷贝构造函数也没声明赋值操作符,结果将变得十分可怕,通常会产生很多不良后果。看下面例子:
class String {
public:
String(const char *value);
~String();
... // 没有拷贝构造函数和operator=
private:
char *data;
};
String::String(const char *value)
{
if (value) {
data = new char[strlen(value) + 1];
strcpy(data, value);
}
else {
data = new char[1];
*data = '/0';
}
}
inline String::~String() { delete [] data; }
如果这样定义两个对象:
String a("Hello");
String b("World");
如果进行下面的赋值:
b = a;
赋值产生的可怕结果如下:
a: data --------> "Hello/0"
b: data --------> "Hello/0" --/ "World/0"
由此可看出b曾经只想的地址将永远得不到删除释放,这就产生了内存泄露。
牢记:为需要动态分配内存的类声明拷贝构造函数和赋值操作符。
当然在为类定义赋值操作符是还有很多注意事项,尤其是涉及到类继承和成员初始化时。如果一个类B继承自类A,那么B的赋值运算符必须显示对其父类A进行赋值,否则会产生一些很严重的后果。例如:
B& B::operator=(const B& aBobject)
{
if (this == &rhs) return *this;
this->B::operator=(aBobject);
return *this;
}
但是,如果基类的赋值运算符是编译器自己生成的,那么,上述代码可能会被某些编译器拒之门外。为了通用性,必须改进上述代码。
B& B::operator=(const B& aBobject)
{
if (this == &rhs) return *this;
static_cast<B&>(*this) = aBobject;
return *this;
}
从上面的代码可以看到,返回的都是引用类型,这样做的目的可以避免产生一些临时对象,进而避免了因为没有显示地定义拷贝构造函数而带来的一些指针指向错误的可能。此外,当自己定义赋值运算符时,必须返回*this,如果不这样做,将将会导致不能连续赋值和隐式转换不能进行等严重问题。