Effective C++读书笔记之五

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

            地产中介商卖的是房子,一个中介软件系统自然而然想必有个class用来描述待售房屋:

class HomeForSale{...} ;

           每一个真正的地产中介商都会说,任何一笔资产都是天上地下独一无二,没有两笔完全想象。因此我们也认为,为HomeForSale对象做一份副本有点没道理。你怎么可以复制某些先天独一无二的东西呢?因此,你应该乐意看到HomeForSale的对象拷贝动作以失败收场:

HomeforSale h1 ;

HomeForSale h2 ;

HomeForSale h3(h1) ;// 企图拷贝h1---不该并通过编译

h1 = h2 ;// 企图拷贝h2---也不该通过编译

          啊呀,阻止这一类代码的编译并不是很乐观。通常如果你不希望class支持某一特定机能,只要不声明对应函数就是了。但这个策略对copy构造函数和copy assignment操作符却不起作用,因为如果你不声明它们,而某些人尝试调用它们,编译器会为你声明它们。

         这把你逼到了一个困境。如果你不声明copy构造函数或copy assignment操作符,编译器可能为你产出一份,于是你的

class支持copying。如果你声明它们,你的class还是支持copying。但这里的目标是不支持copying!

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

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

          将这个伎俩实行于HomeForSale也很简单:

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

} ;

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

class HomeForSalle:private Uncopyable

{

...// class不再声明copy构造函数或copy assign操作符

} ;

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

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

请记住:

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值