拷贝构造函数、拷贝赋值运算符、析构函数

来源:互联网 发布:几个程序员去吃饭 编辑:程序博客网 时间:2024/05/23 23:57

拷贝构造函数,如果一个构造函数的第一个参数是自身类类型的引用,任何额外参数都有默认值,则此构造函数是拷贝构造函数。拷贝构造函数的第一个参数必须是引用类型。

class{

publicFoo();//默认构造函数

Fooconst Foo&);//拷贝构造函数};

如果我们没有为一个类定义拷贝构造函数,编译器会为我们定义一个合成的拷贝构造函数。合成的拷贝构造函数会将其参数的非static成员逐个拷贝到正在创建的对象中。

拷贝构造函数参数必须是引用类型,如果参数不是引用类型,调用永远不会成功,因为调用拷贝构造函数必须拷贝它的实参,但为了拷贝实参,又需要调用拷贝构造函数,循环往复。

拷贝赋值运算符,如果类未定义自己的拷贝赋值运算符,编译器会为它合成一个合成拷贝赋值运算符。类似于运算符重载。

析构函数,析构函数执行与构造函数相反的操作;析构函数释放对象使用的资源,销毁对象的非static数据成员。析构函数是类的一个成员函数,名字由波浪号接类名构成。它没有返回值也不接受参数,如:~Foo();由于析构函数没有参数,因此不能被重载,一个类只有唯一的析构函数。如下:

{//新的作用域

Sales_data *p=new Sales_data;//p是一个内置指针

auto p2=make_shared<Sales_data>(); //p2是一个shared_ptr

Sales_data item(*p);          //拷贝构造函数将*p拷贝到item

vector<Sales_data>vec;       //局部对象

vec.push_back(*p2);         //拷贝p2指向的对象

delete p;              //p指向的对象执行析构函数

}//退出局部作用域;对itemp2vec调用析构函数,销毁p2若引用计数为0,对象被释放,销毁vec会销毁它的元素

当一个编译器未定义自己的析构函数时,编译器会为它定义一个合成析构函数。

当确定一个类是否要定义自己的拷贝控制成员时,一个基本原则是首先确定这个类是否需要一个析构函数。如果这个类需要一个析构函数,可以肯定他也需要一个拷贝构造函数和一个拷贝赋值运算符。一个类在构造函数中动态分配内存,则合成析构函数不会delete一个指针数据成员,因此此类需要定义一个析构函数来释放构造函数分配的内存。但此时若使用合成版本的拷贝构造函数和拷贝赋值运算符就会有可能出错,如:

class Hasptr{

publicHasptrconst string &s=string()):psnew strings)),i0{}

~Hasptr{delete ps}

//错误:Hasptr还需要一个拷贝构造函数和一个拷贝赋值运算符

}

构造函数中分配的内存将在Hasptr对象销毁时被释放。但使用了合成的拷贝构造函数和拷贝赋值运算符。这些函数简单的拷贝指针成员,意味着多个Hasptr可能指向相同的内存,如:

Hasptr fHasptr hp{ //传值,对象被拷贝,所含的指针也被拷贝,但内存没变

Hasptr ret=hp

return ret;  //返回ret的一份拷贝,rethp被销毁}

函数f返回,hpret都会调用Hasptr的析构函数。但这两个对象包含相同的指针值,因此会导致指针被delete两次,是一个错误。而且如:

Hasptr p(“some values”);fp);//f结束时,p.ps指向的内存被释放

Hasptr qp);  //现在pq都指向无效内存