effective C++ 条款05 to 条款12

这个部分的概念貌似简单,实则极为核心,是最基础的东西。因为构造函数,析构函数,拷贝构造函数,拷贝赋值函数很多时候都是默默调用的,比如,返回对象,堆上新建数组,等等,我们必须保证这几个基本的函数能够正常工作,才谈的上让类发挥作用,否则,只会陷入无尽的沉思

 

条款05:

显然,C++默认会有以下一些函数(都是public 和 inline的):

1、默认构造函数:所有的非内置类型成员不初始化,所有类类型成员用其默认构造函数初始化

2、析构函数:默认是非虚的

3、拷贝构造函数:Empty(const Empty & rhs) {...} 这是单纯将来源对象的每一个non-static成员变量拷贝到目标对象

4、拷贝赋值函数:与拷贝构造函数基本一致,只是类中的引用常量不能赋值(不然就违反了这两个类型的意义了)。如果这个类要调用基类的拷贝构造函数,但是那个东西就private得,也编译器也不会生成copy assignment

 

条款06:怎样让一个类不能拷贝

把copy ctor 和 copy assignment 弄成private的,这样就可以了,一调用,编译的时候都错

上面的缺点:类的member function 和 友元 不受这个限制,还是有风险

解决方案1:只声明,不定义,那么,如果再member func 和 友元中用了上面两个函数,编译OK, link error

解决方案1的缺点:能早发现就早发现,最好是在编译的时候及早发现,早发现早治理

 

解决方案2:制作专门的类用于防止拷贝(就是个接口,但是C++中没有接口的概念)

class Uncopyable{

protected :

    Uncopyable() {}   // 子类还是可以构造和析构

    ~Uncopyable(){} // 析构函数不用虚了

private:

     Uncopyable(const Unsopyable&) ;  //但是组织copy,即使是子类

     Uncopyable& operator=(const Uncopyable &) ;

};

 

运用:class HomeForSale : private Uncopyable{} ; // 注意这个地方不用public 继承了

 

 

条款07:多态基类 果基类的目的是为了多态,那么析构函数要虚

由来:当derived class 对象经由一个base class 指针被删除,而base class得析构函数不是虚的,其结果未定义,通常是base被销毁了,derived 的非base部分没有被销毁。

 

我们用virtual的目的,是为了子类实现个性化,因此,虚的目的是确有其用的,不能瞎虚,那样会影响移植性和代码性能

至少一个类里面有一个虚函数,我们才把这个类的析构函数弄成虚的。

书上一句话:"果你曾经企图继承一个标准容器或者任何其他带有non-virtual析构函数的class,拒绝诱惑"

如果你想让一个类成为抽象类,但是手头有没有纯虚函数,那么用析构函数做纯虚函数是个好主意

 

如果一个类的作用只是为了做基类,不是为了多态,那么就不需要virtual 析构函数,例如上面的 Uncopyable

 

条款08:析构函数千万表抛异常

这个条款我觉得写得不好,应当参考《more effective C++》item 11 我做个小小的总结。

1、两种情况下析构函数会被调用:一个对象的声明周期自然地结束;或者是前面抛了个异常,由于异常处理机制,导致对象被删除。

我们在析构函数中无法判断是那种情况激活了析构函数,如果析构再抛一个,那么控制权交到了函数外,C++将调用terminate函数。

2、我们说,析构不能抛出异常,主要是因为,我们捕捉不到,那样析构抛出的异常肆意乱跑,不受控制,导致不明确行为或者程序终止。

3、针对上述的问题,我们有的解决方法:析构函数吞下任何异常,catch(...)

4、因此,如果要对析构函数中的某个异常做出反应,我们应该在类中提供另外的接口,而不是在析构函数中。

 

条款09:构造函数和析构函数中不要调用虚函数

1、因为在base class构造期间,virtual 函数不是virtual 函数(间接调用也不行)
2、你要想在构造函数中实现虚的效果,只能依靠derived class做信息上传(base 的信息由derived 提供),书上的那个例子挺恶心的,我觉得,这个条例有用的地方在于:有些设计模式,例如模板模式,就有可能出现上述的情况,但是,我觉得,可以弄个单独的函数出来,但是,不要把这个放在构造或则会析构中。

 

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

该条款适用所有的标准赋值形式。为什么呢?因为这样可以实现连锁赋值

x=y=x=15

 

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

1、没有哪个SB会直接自己给自己赋值,总是发生在无声无息中。为了代码健壮性,应当有所考虑。

有以下几种解决方案:

方案1:加上“证同测试”

Widget& Widget::operator=(const Widget& rhs)

{

    if(this == &rhs)  return * this  // 证同测试

    delete pb ;

    pb = new Bitmap(*rhs.pb) ;

    return *this ;

}

 这个版本存在异常方面的问题,加入new 抛出异常,pb将指向被删除的地址。

 

方案2:如果异常安全,往往自动获得“自我赋值安全”的回报,所以,很多人将焦点放在异常安全

Widget& Widget::operator=(const Widget& rhs)

{

    Bitmap* pOrig = pb ;

    pb = new Bitmap(*rhs.pb) ;

    delete pOrig ;

    return *this ;

}

 

方案3:copy and swap(后面细讲,听复杂,我不是特别懂

class Widget{

    void swap(Widget& rhs) ;  // 前提是有一个函数,实现*this和rhs得数据交换

}

Widget& Widget::operator=(const Widget& rhs)

{

    Widget temp(rhs) ;

    swap(temp) ;

    return *this ;

}

 

条款12:复制对象别忘了每一样

copying函数一般有两个:copy 构造函数 和 copy assignment

如果我们自己去实现copy而不用编译器默认的,我们就要认真复制对象的每一项,包括基类的东西

我一开始以为要讲:深度复制,其实这个条款不是说这个的

 

记住:copying函数应该确保复制“对象内所有成员变量” 及 “所有base class 成分”

不要尝试以某个copying函数实现另一个copying函数,应该将共同的机能放进第三个函数,由两个copying函数去调用

 

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值