条款11:在operator=中处理自我赋值的现象
虽然我们在平时可能不会出现显示自我赋值的现象,当加入指针或者引用时,可能会出现不同的指针或引用指向同一对象(对象的不同别名),这时候我们就得考虑对象是否是同一个;考虑到要保证系统的自我赋值安全性和异常安全性的角度,可以采用的方案是:将原来的被赋值对象做一个副本,然后让被赋值对象指向复制对象,最后删除副本即可,代码如下:
widget& widget::operator=(const widget & rhs){
bitmap *porig=pb; //pb为类widget的一个类型为bitmap的指针
pb=new bitmap(*rhs.pb);
delete porig;
return *this;
}
最好的方法是采用copy and swap的方案,代码如下:(方案1采用传引用的方法,方案2采用传值的方法
void swap(widget &rhs) {....}; //交换rhs和*this的数据;
方案1: widget &widget::operator=(const widget & rhs){ 方案2: widget&widget::operator=(widget rhs)
widget temp(rhs); {
swap(temp); swap(rhs);
return *this; return *this;
} }
条款12:复制对象时勿忘其每个成分
当自己定义copy函数时(copy构造和copy assignment),编译器不再给我们提供新的copy函数,因此在拷贝的过程中,我们要确定所有的成员变量都被拷贝;
当我们为class增添新的成员变量时,我们需要修改我们所有的copy函数,构造函数,采用继承的方式可以避免代码的过多修改,但是你必须保证base class的成员变量也被初始化,也就是在derived class的拷贝函数初始化列表或者函数体中调用base class的拷贝函数;如果不这样做,derived class在构造copy函数的时候会调用base class的default copy函数,但是base class的copy函数是被阻止的,因此此时编译器会报错;
结论:当我们写一个自定义的copy函数的时候,确定几点:1)复制所有的local成员变量;2)调用所有base class内适当的copy函数;
不要令copy assignment去调用copy构造函数;也不要让copy构造函数去调用copy assignment;
当copy assignment和copy 构造函数有相近的代码时,可以把相同的代码变成新的成员函数,这样的函数通常为private且常命名为init;
条款13:以对象管理资源
以singleton(单例模式)的factory function为例,函数返回的是一个heap-base的指针,用户在使用完这个对象之后需要把这个指针删除,如何保证这个指针所指向的资源被删除呢?可选用的方法是把:把资源放进对象中,利用C++的“析构函数自动调用机制”保证资源被释放;
C++标准程序库中提供auto_ptr(类指针对象,也称为智能指针),其析构函数自动对其所指对象调用delete,常见的智能指针有unique_ptr,shared_ptr;weak_ptr;
以对象管理资源的两个关键想法:1)获得资源后立刻放进管理对象内,实际上“以对象管理资源”的观念通常被称为“资源取得时机便是初始化时间(resource acquisition isinitialization RAII)”;2)管理对象运用析构函数确保资源被释放(在析构函数释放资源时,可能会出现异常,正确处理析构函数中抛出异常的情况);
auto_ptr的特性是被销毁(调用copy函数时会发生)时自动删除它的所指之物,并将自己的指针指向nullptr,因此不要让对个auto_ptr指向同一个对象,如果这样可能会出现对象被删除一次以上;auto_ptr的性质是始终只有一个指针取得资源的唯一拥有权;
auto_ptr的替代方案是:引用型计数指针(reference-counting smart pointer,RCSP),该指针特性就是持续追踪多个指针指向同一资源,并在无人指向该资源时释放资源,但是RCSP无法打破环形引用(也就是类中互相指向的问题,可用weak_ptr来解决这一现象);
由于以对象管理资源在析构函数中执行的是delete而非delete[],因此动态分配的数组无法通过这种方式来实现内存释放,只能通过自己手工释放内存资源;