effective c++读书笔记二——构造/析构/赋值运算

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

声明了一个构造函数,编译器将不再为它创建default构造函数。

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

如果你不希望某个class支持某一特定功能函数,比如不希望某个类具有copy构造函数和copy assigment函数,你不声明,但是编译器会为你默认生成,若你不需要对应的函数,请明确拒绝。

l         将对应的函数声明为private,明确告诉编译器阻止生成对应的函数。

l         比较常用的方法,采用继承机制,将父类对应的两个函数声明为private,这个在boost有对应的类。

6. 条款7:为多态基类声明virtual析构函数

1)当derived class对象经由一个base class指针被删除,而该base class带一个non-vritual析构函数,其结果——实际执行时通常发生的是对象的derived成分没被销毁。会造成一个“局部销毁”。

       2)只有当class 内含至少含有一个virtual函数才为它声明virtual析构函数,并且该bases class是一个带多态性质的。无端将所有classes的析构函数声明为virtual,就像从未声明一样是错误的。

       3classes设计目的不是作为base classes使用,不是为了具备多态性质,比如标准的stringSTL容器都不被设计为base classes使用,有些classes设计目的是作为base classes但是并非为了多态用途,比如uncopyable函数。这些不能将构造函数声明为virtual

7 条款08 别让异常逃离析构函数

       1)析构函数绝对不要吐出异常。记住例子std::vector<Widget> v;假设v中第1个元素吐出异常,而其他元素也应该销毁,否则会发生资源泄露。假设第二个widget析构函数也发生异常,两个异常同时存在,程序若不是结束执行就是导致不明确行为。

       2)若析构函数必须执行一个动作,而该动作有可能会失败抛出异常,该怎么办?

比如一个class负责数据库连接,析构函数中调用close()关闭数据库连接的函数。

有两种解决办法,但是这两种解决办法都不是最好的办法,因为他们都无法对“导致close抛出的异常”的情况做出反应:

l         Close函数抛出异常就结束程序,通常可以用abort完成。

DBConn::~DBConn()

{

    try{db.close();   }

    catch (CException* e){

       //制作运转记录,记下对close的调用失败;

       std::abort();    

    }

} 

l         吞下调用close而发生的异常:

DBConn::~DBConn()

{

    try{db.close();   }

    catch (CException* e){

       //制作运转记录,记下对close的调用失败;         

    }

} 

最好的解决办法:

class DBConn{

public:

    void close()

    {

       db.close();

       closed = true;

    }

    ~DBConn()

    {  

       if(!closed){

           try{db.close();   }

           catch (CException* e){

               //制作运转记录,记下对close的调用失败;

           }

       }

    }

private:

    bool closed;

}

       如果客户需要对某个操作函数运行期间抛出异常做出反应,提供一个普通函数让用户调用。

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

       1base class构造函数期间virtual函数绝不会下降到derived class阶层。Derived classbase class构造期间,对象的类型是base class而不是derived class。相同的道理也适用于析构函数,derived class析构函数开始执行,对象的derived class成分就呈现未被定义的状态,如果base class中调用,就成为一个base class对象。

       2)确定你的构造函数和析构函数都没有调用virtual函数。

       3)解决办法:无法使用virtual函数从base classes向下调用,在构造函数期间,你可以藉由“令derived classes将必要的构造信息向上传递至base class构造函数”替换加以弥补。

9.条款10:令operator =返回一个reference to* this

为了实现连锁赋值形式:如:x = y = z = 10; 赋值操作符必须返回一个reference指向操作符的左侧实参。

如下:

class Widget{

public:

    ......

    Widget& operator =(const Widget& rhs){

           ......

           return* this;

    }

    ......

};

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

1)首先不处理自我赋值会发生的异常分析。如果不处理自我赋值问题是,operator =函数内的*this rhs为同一个对象,当用析构时会出现delete会删除一个已经销毁的对象。

2)需要采用证同测试,传统的做法是采取检测是否为自我赋值,加入如下判断:

if(this ==&rhs) return *this;

delete pb;

pb = new Bitmap(*this.pb);

return * this;

        但是这种方式,不具有异常安全,如果new导致异常

3)具备异常安全的做法,是先记住原来内容,再new一个原内容的复件,删除原先的指针。具体做法如下:

     bitmap* pOrig = pb;

     pb = new Bitmap(*rhs.pb);

     delete pOrig;

     return * this;

     此办法具有异常安全,但是缺点效率不高。

(4) 最好做法,采用copy and swap的技术。

      widget temp(rhs);

      swap(tmep);

      return *this;

 

10 .条款12:复制对象时勿忘其每一个成分

 1)当你为derived class撰写copying函数时,必须小心的复制base class成分,那些成分往往是private的,无法直接访问,让derived classcopying函数调用相应的base class函数。

2copy assignment操作符调用copy构造函数不合理的,反过来调用也是不合理的。如果两个函数有相似的代码,正确的做法是将相同的代码建立一个新的函数给两者调用,一般并声明为private并命名为init

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值