浅论析构函数,拷贝构造函数和赋值运算符

时间:2022-12-23 07:56:35


1:定义

构造函数:用于初始化成员变量,构造类变量。

析构函数:用于释放类成员占用的内存资源。

拷贝构造函数:构造函数的一种,用于从一个已有的对象拷贝为另外一个对象(复制)。

赋值运算符(=):用于两个对象(成员变量)之间的赋值(值的复制)。

2:例程:

如下:

class Person{
public:
Person(){
_id = 1;
_name="hundun";
_house = new double[120];//建造一个120平米的房子
}
~Person(){
delete []_house;
}
private:
int _id;
string _name;
double *_house;
};

此对象并没有显式的拷贝构造函数和赋值运算符,编译器会为类构造一个隐式的拷贝构造函数和赋值运算符。隐式的拷贝构造函数和赋值运算符进行类全部成员的直接复制操作。

但是有时不得不定义显式的拷贝构造函数和赋值运算符。

三原则:任何时候,只要你提供了析构函数、拷贝构造函数或赋值运算符中的一个,你通常需要三个都提供。

就如上例所示,运行以下代码时:

int main()
{
Person p1;
{
Person p2(p1);
// Person p3=p1;
}//使用块进行强制析构调用
Return 0;
}

当运行Person p2(p1);时,执行默认的拷贝构造函数,编译器将p1对象的_id赋给p2对象的_id(OK),将p1对象的_name赋给p2对象的_name(OK);p1对象的指针_house赋给p2对象的指针_house,于是错误出现了,他们同时指向了一块相同的堆内存。

当临时块语句执行完,对象p2析构,它释放自己的_house内存,于是p1内存指向了一块已经释放的内存,这是不允许的。同样赋值运算符也是如此。所以需要显示的拷贝构造函数和赋值运算符。

3:显示构造拷贝构造函数和赋值运算符

显式的拷贝构造函数:

Person::Person(const Person&another):_id(another._id),_name(another._name){
this->_house= new double[120];//为新的对象申请另外一块内存,于此对象各操作各的内存块
for(int ix=0;ix<120;++ix)
this->_house[ix]=another._house[ix];//拷贝久对象的指针元素给新对象
}

显式的赋值运算符:

Person& operator=(const Person&another){

  if(this!=&another){

  _id=another._id;

_name=another._name;
delete []_house;//释放原来构造产生的
_house= new double[120];//申请新的内存,并拷贝值
for(int ix=0;ix<120;++ix)
_house[ix]=another._house[ix];
}
return *this;//返回调用的对象
}

4:其他:

Person  pp1;

Person  pp2 = pp1;//注意:这是调用拷贝构造函数而非调用赋值运算符。

Person  pp3;

Person  pp4;

pp4 = pp3;   //这里则是调用的赋值运算符。

总结:两个对象构造完了(都已经调用了构造函数),再用=,则是赋值运算。

反之,没有先构造而直接用=,则是拷贝构造。(Person  pp2 = pp1;)