Effective C++条款06——若不想使用编译器自动生成的函数,就该明确拒绝(构造/析构/赋值运算)

通常如果你不希望class支持某一特定机能,只要不声明对应函数就是了。但这个策略对copy 构造函数和copy assignment操作符却不起作用,因为条款5已经指出,如果你不声明它们,而某些人尝试调用它们,编译器会为你声明它们。

这把你逼到了一个困境。如果你不声明copy构造函数或copy assignment操作符,编译器可能为你产出一份,于是你的class支持copying。如果你声明它们,你的class还是支持copying。但这里的目标却是要阻止 copying!

答案的关键是,所有编译器产出的函数都是public。为阻止这些函数被创建出来,你得自行声明它们,但这里并没有什么需求使你必须将它们声明为public。因此你可以将copy构造函数或copy assignment操作符声明为private。藉由明确声明一个成员函数,你阻止了编译器暗自创建其专属版本;而令这些函数为private,使你得以成功阻止人们调用它。

加delete也可以,陈硕moduo库就是这样处理的。

一般而言这个做法并不绝对安全,因为member函数和 friend函数还是可以调用你的private函数。除非你够聪明,不去定义它们,那么如果某些人不慎调用任何一个,会获得一个连接错误(linkage error)。“将成员函数声明为private而且故意不实现它们”这一伎俩是如此为大家接受,因而被用在C++ iostream程序库中阻止copying行为。是的,看看你手上的标准程序库实现码中的ios_base,basic_ios和sentry。你会发现无论哪一个,其copy构造函数和 copy assignment操作符都被声明为private 而且没有定义。

class HomeForSale {
public:
    // ...

private:
    // ...
    HomeForSale(const HomeForSale&);            // 只有声明
    HomeForSale& operator=(const HomeForSale&);

};

或许你注意到了,我没写函数参数的名称。唔,参数名称并非必要,只不过大家总是习惯写出来。这个函数毕竟不会被实现出来,也很少被使用,指定参数名称又有何用?

有了上述class定义,当客户企图拷贝HomeForSale对象,编译器会阻挠他。如果你不慎在member函数或friend函数之内那么做,轮到连接器发出抱怨。

将连接期错误移至编译期是可能的(而且那是好事,毕竟愈早侦测出错误愈好),只要将copy构造函数和 copy assignment操作符声明为private就可以办到,但不是在HomeForsale自身,而是在一个专门为了阻止 copying动作而设计的base class内。这个base class非常简单:

class Uncopyable {
protected:
    Uncopyable() {  }                // 允许Derived对象构造和析构
    ~Uncopyable() {  }

private:
    Uncopyable(const Uncopyable&);    // 但是阻止copying
    Uncopyable& operator=(const Uncopyable&);
};

为求阻止HomeForSale对象被拷贝,我们唯一需要做的就是继承Uncopyable:

class HomeForSale : private Uncopyable {
    // ...

};

// clas不再声明copy构造函数或者拷贝赋值函数

这行得通,因为只要任何人——甚至是member 函数或friend 函数一尝试拷贝HomeForsale对象,编译器便试着生成一个copy构造函数和一个copy assignment操作符,而正如条款12所说,这些函数的“编译器生成版”会尝试调用其 base class的对应兄弟,那些调用会被编译器拒绝,因为其 base class 的拷贝函数是private。

好思路

Uncopyable class 的实现和运用颇为微妙,包括不一定得以public继承它(见条款32和39),以及Uncopyable的析构函数不一定得是virtual(见条款7)等等。Uncopyable不含数据,因此符合条款39所描述的empty base class optimization资格。但由于它总是扮演base class,因此使用这项技术可能导致多重继承(译注:因为你往往还可能需要继承其他class)(多重继承见条款40),而多重继承有时会阻止 empty base class optimization(再次见条款39)。通常你可以忽略这些微妙点,只像上面那样使用Uncopyable,因为它完全像“广告”所说的能够正确运作。也可以使用 Boost(见条款55)提供的版本,那个class名为noncopyable,是个还不错的家伙,我只是认为其名称有点……呃……不太自然。

请记住
为驳回编译器自动(暗自)提供的机能,可将相应的成员函数声明为private并且不予实现。使用像Uncopyable这样的base class 也是一种做法。

你可能感兴趣的:(Effective,C++,c++,开发语言,keep,studying,Effective,C++)