读书笔记《Effective C++》条款06:若不想使用编译器自动生成的函数,就该明确拒绝

xiaoxiao2021-02-27  549

所有编译器产出的函数都是public。如果不想让编译器自动生成这些函数,得自行声明它们,可以将copy构造函数或copy assignment操作符声明为private,这样就阻止了编译器暗自创建的版本,而且令这些函数为private,得以成功阻止人们调用它们。

一般而言这个做法并不绝对安全,因为member函数和friend函数还是可以调用private函数。有种办法:只声明这些private函数,但并不去定义。那么如果某些人不不慎调用任何一个,都将会得到一个链接错误。“将成员函数声明为private而且故意不实现它们”这一伎俩是如此为大家接受,因而被用在C++ iostream程序库中阻止copying行为。

例子:

class HomeForSale { public: HomeForSale() { } private: HomeForSale(const HomeForSale& rhs);//只有声明,没有定义 HomeForSale& operator=(const HomeForSale& rhs);//只有声明,没有定义 }; HomeForSale h1; HomeForSale h2; HomeForSale h3(h1);//企图拷贝h1,使用private copy构造函数,编译不通过 h1 = h2;//使用private assignment操作符,编译不通过

上述class定义,当用户企图拷贝HomeForSale对象,编译器会阻挠他。如果不慎在member函数或friend函数之内那么做,就会轮到连接器发生报错。

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

class Uncopyable { protected: Uncopyable() {} //允许derived对象构造和析构 ~Uncopyable() {} private: Uncopyable(const Uncopyable&); //但阻止copy Uncopyable& operator=(const Uncopyable&); };

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

class HomeForSale : private Uncopyable { //class不再声明copy构造函数或copy assignment操作符 };

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

Uncopyable class的实现和运用颇为微妙,包括不一定得以public继承它,以及Uncopyable的析构函数不一定得是virtual等等。Uncopyable不含数据,符合empty base class optimization资格。但由于它总是扮演base class,因此使用这项技术可能导致多重继承,因为你往往还可能需要继承其他class,而多重继承有时会阻止empty base class optimization。通常你可以忽略这些微妙点,只要像上面那样使用Uncopyable,因为它能够正常运作。也可以使用Boost提供的版本:noncopyable。

要点:

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

转载请注明原文地址: https://www.6miu.com/read-533.html

最新回复(0)