C++书写拷贝构造函数,重载赋值操作符和clone函数需要注意有关问题
对于C++类:显示地写出拷贝构造函数,重载赋值操作符和析构函数是良好的习惯,但在写构造函数时需要注意一些容易出现的错误,如下面的代码:
#include <iostream>
using namespace std;
class M{
public:
M(){}
M(const M &m){
cout<<"copy construtor"<<endl;
operator =(m);
}
M operator =(const M &m){ //问题出在此处
cout<<"operator ="<<endl;
return *this;
}
};
int main() {
M m1;
M m2;
m2=m1;
return 0;
}
在下面三种情况下会调用拷贝构造函数:
(1)用一个已经实例化了的该类对象,去实例化该类的另外一个对象;
(2)用该类的对象传值的方式作为一个函数的参数; (3)一个函数返回值为该类的一个对象。 特别地,对于语句
M m; M mm=m; 属于(1)情况,即语句M mm=m;调用的是拷贝构造函数,而不是构造函数。 但在重载=操作符时,返回值不是引用类型将导致程序运行出现严重问题。即如果出现上面会调用拷贝构造函数的三种情况之一,或者使用=操作符时,拷贝构造函数和operator =将循环递归调用,导致程序出现死循环。原因是拷贝构造函数和operator =之间不断地重复调用。 解决办法:将operator =的返回类型改为引用类型M&,此时调用operator =时不会去调用拷贝构造函数。 还有,若要写clone时,若通过下面的方式: M clone(){ cout<<"clone"<<endl; return *this; } 前提是拷贝构造函数不能调用clone来完成拷贝,否则出现上面同样的问题,下面的代码就会出现这样的问题 M(const M &m){ cout<<"copy construtor"<<endl; clone(); } 总之,在写这些函数时,要特别留意彼此的调用关系。 以下是我的惯用写法: (A)对于拷贝构造函数和重载=操作符 M(const M &m){ cout<<"copy construtor"<<endl; operator =(m); } M& operator =(const M &m){ //问题出在此处 cout<<"operator
="<<endl; /* 此处写上成员数据的拷贝 */ return *this; } 这里写成了inline函数,只是方便说明问题,其实不必非要这么写,可以采取先声明,后定义的常规方法。 (B)对于clone函数 声明: virtual M clone(); //考虑继承时的多态 定义: M M::clone(){ cout<<"clone"<<endl; //将在调用处直接调用构造函数,效率高,避免返回局部变量,更安全 return M(); }