Effective c++ 2 5…12

条款005:了解C++默默编写并调用哪些函数

如果有引用或者const成员变量,编译器拒绝合成赋值操作符。

template <typename T>
class NameObject{
public:
    NameObject(string& name,const T& value):
    nameValue(name),objectValue(value)
    {    }
private:
    string& nameValue;//&引用变量
    const T objectValue;
};
//warning C4512: 'NameObject<T>' : assignment operator could not be generated
int main() { 

    string newDog("Per");
    string oldDog("Sat");
    NameObject<int> p(newDog,2);
    NameObject<int> s(oldDog,36);
     p = s;// error C2582: 'operator =' function is unavailable in 'NameObject<T>'
    }

编译器不知道如何赋值不能修改的引用和const。

条款006:若不想使用编译器自动生成的函数,就该明确拒绝

抵制拷贝

1 private:
2     __CLR_OR_THIS_CALL basic_ios(const _Myt&);    // not defined
3     _Myt& __CLR_OR_THIS_CALL operator=(const _Myt&);    // not defined
4 
5 //basic_ios类  声明了private的 拷贝构造和 赋值操作符  不定义

声明:让编译器不能合成默认的

Private:让外部不能使用

不定义:让内部和友元也崩想用


uncopyable class 基类

1 class uncopyable{
 2 protected:
 3     uncopyable(){}
 4     ~uncopyable(){}
 5 private:
 6     uncopyable(const uncopyable&);
 7     uncopyable& operator=(const uncopyable&);
 8 };
 9 
10 class HomeForSale: private uncopyable{
11 //HomeForSale类抵制拷贝
12 };
13 //error C2248: 'uncopyable::uncopyable' : cannot access private member declared in class 'uncopyable'

条款007:为多态基类声明virtual析构函数

没有虚析构,基类指针指向派生类对象时,delete基类指针,会调用基类的析构函数,很有可能只析构掉基类部分,残余派生类部分。100%是未定义行为。

虚函数有4字节虚表。

如果你不打算使用多态,这东西就不用定义。

条款008:别让异常逃离析构函数

最坏也要在析构函数里catch住任何异常。

如果可能的话,最好在析构前用普通函数打扫战场。降低析构函数里发生异常的可能性。

1 class DBConnection{
 2 public:
 3     static DBConnection create();
 4     void close();//断开连接的接口提供给用户了,但用户不一定会用。发生没断开链接就析构了的问题。
 5 };
 6 //为确保析构前调用close()而创建的类
 7 class DBConn{
 8 public:
 9     void close(){//提供给客户断开连接的接口 用户可以捕捉这个接口的异常,进行处理。
10         db.close();//而不是让析构函数去调用可能发生异常的函数close
11         closed = true;//如果这里成功了,析构函数就不再调用close
12     }
13     ~DBConn()
14     {
15         if(!colsed){//如果还是沦落到要析构函数来调用可能异常的函数
16             try{db.close();}
17             catch (...) {
18                 cout<<;  //catch住任何异常,就可以了。不要从析构函数抛出异常。在这里吞掉。做记录或者调用abort()什么的也可以。
19             }
20         }
21     
22     }
23 private:
24     DBConnection db;
25     bool closed;
26 };
27 
28 DBConn dbc(DBConnection::create());//操作dbc对象  dbc对象析构的时候保证断开连接之后再析构。

条款009:绝不在构造和析构过程中调用virtual函数

基类构造函数->派生类构造函数->…->派生类析构函数->基类析构函数。

如果基类在构造函数里调用了virtual函数,创建派生类对象的时候,先调用基类构造,此时,还没有派生类对象的时刻(此时,对象的类型就是基类类型),用的是virtual函数基类的版本(如果定义了,没定义就报连接错),你本意是创建派生类对象的,这就错了。你的派生类构造函数中还会调用这个virtual函数吗?不一定。派生类可能定义了这个virtural函数的派生类版本,但是没有在自己的构造函数中调用。

如果在基类的析构函数里调用了virtual函数,析构派生类对象的时候,先析构派生类对象,再析构基类对象,此时,派生类对象已经不存在的时刻,用的是virtual的基类的版本。

如果想构造函数里调用的函数实现多态那种效果怎么办?

这个要调用的函数不是虚函数了,那么派生类构造函数中,把派生类所拥有的特殊信息传给基类的构造函数(构造函数可以有各种形参),来展现特别性。

条款10:令operator= 返回reference *this

因为标准里的=操作符有 连锁赋值的功能。

int a=b=c=15;

那么类的重载赋值操作符也保留这个功能,依靠operator=返回自身类型的引用来实现。即

return *this;

条款11:在operator=中处理“自我赋值”

实现异常安全性 顺便实现自我赋值安全。

例如:pb成功获得正确的新对象之前不删除旧对象。这种策略就同时达到了异常安全性和自我赋值安全性。

1 Widget& Widget::operator=(const Widget& rhs)
2 {
3     Bitmap* pOrig = pb;    //    记住原先的pb
4     pb = new Bitmap(*rhs.pb);//pb获得新值——源头的副本
5     delete pOrig;//删除原先的pb
6     return *this;
7 }

如果自我赋值,rhs pb pOrig都指向同一个对象。pb指向一个新的对象(拷贝),成功了,之后删除旧的。可以

如果new发生异常,抛出到异常处理部分,不再进行下面的delete rhs指向rhs的, pb指向pb的,pOrig指向pb的。

(异常抛出后 再怎么弄来着?栈展开,被catch住就处理,不能处理就退出函数,编译器保证,每个函数退出的时候,撤销在异常发生之前创建的所有对象(包括自动调用类对象的析构函数),释放函数的局部存储。栈展开期间,释放局部对象所用的内存并运行类类型局部对象的析构函数。)

调用new分配的资源,因异常退出函数时,编译器不会自动释放,不会自动删除该指针。

又例如:copy and swap技术

利用拷贝构造,swap函数。

1 class Widget{
 2 ...
 3 void swap(Widget& rhs);
 4 ...
 5 };
 6 
 7 Widget& Widget::operator=(const Widget& rhs)
 8 {
 9     Widget temp(rhs);
10     swap(temp);
11     return *this;
12 }

利用pass-by-value 传了rhs的副本(还是调用了拷贝构造 构造了temp 只不过叫rhs罢了)

class Widget{
...
void swap(Widget& rhs);
...
};

Widget& Widget::operator=(Widget rhs)
{
    swap(rhs);
    return *this;
}

条款12:复制对象时,复制它的每一个部分

忘了拷贝构造【类类型】成员……

忘了拷贝构造【基类的部分】什么的……会调用基类的默认构造,和你的拷贝源对象的基类部分很可能大相径庭了……

为派生类写copying函数的时候,必须复制其基类部分。往往是private成分,所以考虑用派生类的copying函数调用基类的拷贝构造,调用基类的赋值操作符函数 哦呵呵。

不该令copying assignment操作符调用copy构造函数。能调copy assignment的时候,你的对象显然已经存在了,你还调构造函数,这不是在构造已经存在的对象么…消除重复代码的方法是,建一个新的成员函数给两者调用,这样的函数往往是private且明明为init。
原文链接: https://www.cnblogs.com/boldness/archive/2013/02/22/2922400.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/78646

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月9日 下午6:37
下一篇 2023年2月9日 下午6:37

相关推荐