Effective C++(条款5-12)

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

编译器可以为类暗自创建默认构造函数、复制构造函数、赋值运算符重载函数以及析构函数,所有这些函数都是public且inline。当这些函数被需要(被调用)而用户没有定义这些函数时,它们才被编译器创建出来,编译器创建的析构函数是non-virtual的。


但是,如果你打算在一个含有引用成员const成员的类中支持赋值操作,你就必须自己定义一个赋值运算符重载函数。



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

将相应的成员函数声明为private并且只有声明不予实现使用像Uncopyable这样的基类也是一种做法。

class Uncopyable
{
      protected:
            Uncopyable(){}
            ~Uncopyable(){} //允许继承对象的构造函数和析构函数
      private:
            Uncopyable(const  Uncopyable&);
            Uncopyable& operator=(const  Uncopyable&);  //阻止复制
};




条款7:带多态性质的基类应该声明一个virtual析构函数:

如果类带有任何的virtual函数,它就应该拥有一个virtual析构函数。这种基类的设计目的是为了用来“通过基类接口处理继承类对象”。

类的设计目的如果不是作为基类使用,或不是为了具有多态性,就不该声明virtual析构函数。




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

析构函数绝对不要吐出异常。如果一个被析构函数调用的函数可能抛出异常,析构函数就应该捕捉任何异常,然后吞下他们(不传播)或结束程序。如下:

class DBConnection
{
      public:
          ...
          static DBConnection create();
          void close();//关闭连机,失败时则抛出异常
};


class DBConn()      //这个类用来管理DBConnection对象
{
      public:
          ...
          ~DBConn()
          {
                db.close();
           }
      private:
           DBConnection db;
};



//如果析构函数抛出异常就结束程序,通常是通过调用abort完成
DBConn::~DBConn()
{
      try{db.close();}
      catch(...){
            std::abort();
       }
}



//如果析构函数吞下发生的异常
DBConn::~DBConn()
{
      try{db.close();}
      catch(...){
           制作运转记录,记下对close的调用失败;
       }
}


如果客户需要对某个操作函数运行期间抛出的异常做出反应,那么类应该提供一个普通函数(而非在析构函数中)执行该操作。如下所示:

class DBConn
{
      public:
           ...
           void close()     //供客户使用的新函数
           {
                 db.close();
                 close=true;
            }
            ~DBConn()
           {
                  if(!close)          //关闭连接(如果客户不这么做的话)
                  {
                         try{db.close();}
                         catch(...){
                              制作运转记录,记下对close的调用失败;  //如果关闭动作失败,记录下来并结束程序或吞下异常
                               ...
                          }
                  }
           }
      private:
           DBConnection db;
           bool close;
};
 




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

绝不在构造和析构函数中调用virtual函数,构造函数和析构函数内调用的所有函数也不能是virtual函数,以免发生不适当的virtual函数版本被调用的问题。因为这类调用从不下降至derived class。

在继承类对象的基类对象构造期间,对象的类型是base class而不是derived class,在base class构造期间,virtual函数就不是virtual函数,对象在derived class构造函数开始执行前不会成为一个derived class对象。相同的道理也适用于析构函数,一旦derived class析构函数开始执行,对象内的derived class成员变量就会呈现未定义值,进入base class析构函数后对象就成为一个base class对象。



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



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

确保当对象自我赋值时,operator=有良好的行为。其中技术包括比较“来源对象”和“目的对象”的地址、精心周到的语句顺序、以及copy-and-swap。确定任何函数如果操作一个以上的对象,而其中多个对象是同一个对象时,其行为仍然正确。

 

Widget& Widget::operator=(const Widge& rhs) //注意复制pb之前别删除pb</span>

{
      Bitmap* porig=pb;//记住原来的pb
      pb=new Bitmap(*rhs.pb);//令pb指向*pb的一个副本
      delete porig;//删除原来的pb
      return *this;
}

 //比较“来源对象”和“目的对象”的地址
<pre name="code" class="cpp">Widget& Widget::operator=(const Widge& rhs) 
{
      if(this=&rhs) return *this;
      delete pb;
      pb=new Bitmap(*rhs.pb);
      return *this;
}


 
 确保代码不但“异常安全”而且“自我赋值安全”的一个方案就是使用所谓的copy and swap技术。 

class Widge
{
      ...
      void swap(Widge& rhs);//交换*this和rhs的数据
      ...
};

Widge& Wideg::operator=(const Widge& rhs)
{
      Widge temp(rhs);
      swap(temp);
      return *this;
}




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

类的复制函数应该确保复制“对象内的所有成员变量”及“所有的基类成分”。不要尝试以某个复制函数实现另一个赋值运算符重载函数,反之也是如此,应该将共同机能放进第三个函数中,由两个copying函数共同调用,来消除两函数之间的代码重复。


PC::PC(const PC&rhs):C(rhs),p(rhs.p)
{}

PC& PC::operator=(const PC& rhs)
{
        C::operator=(rhs);
        p=rhs.p;
        return *this;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值