地产中介商卖的是房子,一个中介软件系统想必有个class用来描述待售房屋:
class HomeForSale {...};
为HomeForSale对象做一份副本有点没道理。因此,你应该乐意看到HomeForSale的对象拷贝动作失败:
HomeForSale h1;
HomeForSale h2;
HomeForSale h3(h1); // 企图拷贝h1 不应该通过编译
h1 = h2; // 企图拷贝h2 不应该通过编译
通常如果你不希望class支持某一特定功能,只要不声明对应函数就是了。但这个策略对拷贝构造函数和拷贝赋值运算符却不起作用,因为条款5已经指出,如果你不声明它们,而当调用它们时,编译器会生成它们。
你可以将拷贝构造函数和拷贝赋值运算符声明为private。你阻止了编译器暗自创建其专属版本,而令这些函数为private,你得以成功阻止人们调用它。
一般而言这个做法并不绝对安全,因为成员函数和友元函数还是可以调用你的private函数。将成员函数声明为private而且故意不实现它们这一伎俩被大家接收。如果某些人不慎调用任何一个,会得到一个链接期错误。
将这个伎俩施行于HomeForSale也很简单:
class HomeForSale {
public:
...
private:
...
HomeForSale(const HomeForSale&);
HomeForSale& operator=(const HomeForSale&); // 只有声明
};
有了上述送一,当客户企图拷贝HomeForSale对象,编译器会阻挠它。如果不慎在成员函数或友元函数之内那么做,链接器会报错。
将链接期错误移到编译期是可能的,只要将拷贝构造函数和拷贝赋值运算符声明为private就可以办到,但不是在HomeForSale自身,而是在一个专门为了放置拷贝操作而设计的基类上。这个基类非常简单:
class Uncopyable {
protected:
Uncopyable() {} // 允许derived对象构造和析构
~Uncopyable() {}
private:
Uncopyable(const Uncopyable&); // 阻止拷贝
Uncopyable& operator=(const Uncopyable&);
};
为阻止HomeForSale对象被拷贝,我们唯一需要做的就是继承Uncopyable:
class HomeForSale: private Uncopyable {...};
只要任何人——甚至是成员函数或友元函数——尝试拷贝HomeForSale对象,编译器都会报错,因为base class的拷贝函数是private。
请记住
- 为驳回编译器自动提供的坑你,可将相应成员函数声明为private并且不予实现。使用像Uncopyable这样的基类也是一种做法。