一、三五法则:类需要析构函数的时候,同样也需要拷贝构造函数和拷贝赋值函数。类需要拷贝构造函数和拷贝赋值函数的时候,不一定需要析构函数。如果一个类需要拷贝构造函数,肯定也需要拷贝赋值函数(反之亦然)。
1.需要析构的时候例子:比如类的成员有指向动态内存的内置指针,生成析构函数对指针释放只是简单的销毁,不会delete对应的内存。
此时如果使用了生成拷贝构造函数时,就会简单的复制了类的指针,此时两个类对象的指针就会指向同一个堆中的内存。此时我们需要建立一个新拷贝构造函数。
class test
{
public:
test();
test(const test& hp);
test& operator=(const test &hp);
~test(){delete ps;}
private:
int* ps;
};
test f(test hp) {
test ret = hp; //代码段结束后hp和ret都会被销毁,此时会对成员指针销毁delete两次
return ret;
}
//合成赋值构造函数如下
test& test::operator=(const test& hp) {
test a;
a.ps = hp.ps;
return a;
}
//此时自定义它的拷贝构造和拷贝赋值函数来解决上面的错误
test::test(const test& hp) {
this->ps = new int(*hp.ps);
}
test& test::operator=(const test& hp) {
test a;
a.ps = new int(*hp.ps);
return a;
}
2.如果一个类需要拷贝构造函数,肯定也需要拷贝赋值函数(反之亦然)。
二、阻止拷贝的情况
三、拷贝控制
1.行为像值的类
如果一个类有指针成员,则它的行为要像值时要需要遵循以下规则,
并且拷贝赋值运算时必须要先拷贝指针到临时对象再销毁,然后才能赋值,否则就可能发生以下情况:
2.行为像指针的类
这种行为有一种方法是用类成员的一个指针来实现引用计数,类要遵循以下原则:
此时的拷贝赋值成员函数要注意:先增加右值引用计数,再减少左侧引用计数(即析构函数体),最后进行赋值和返回。
四、交换操作
1.类的自定义类类型交换swap函数的调用优先度大于标准库的swap函数,在类参与了重拍元素顺序的算法时,因为在这个算法需要交换两个对象的元素时会调用swap。
编写自己的swap函数,
上面例子中,交换的是内置指针和内置int成员,同时类没有定义内置类类型的swap,所以使用的是标准库的swap。
在以下例子中,如果使用类类型的swap,则以下是错误的。
错误原因在于交换Foo就会交换成员HasPtr,而交换HasPtr需要特殊的HasPtr类型swap,这里却用的标准库swap。
2.在赋值运算符中使用swap
如果类定义了swap成员,则类就会使用自己的swap来定义赋值运算符,它使用了拷贝并交换的计数。
这个效果跟之前的赋值运算符中,先拷贝一个右值类对象到temp,(再释放指针,再赋值)=自定义swap,再返回是相同效果。
五、左值和右值盘点
1.左右值
2.右值
3.
4.
5.
①第一个sorted函数会陷入死循环,因为ret是左值,且外部函数sorted()和内部的sorted()都是针对左值的函数
②第二个sorted()可以正常运行,外部sorted()是针对左值的,而Foo(*this)是右值。