所有编译器产出的函数都是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也是一种做法。