对于某些单例而言,其对象应是不允许被拷贝的。然而,编译器却会默认为我们生成拷贝构造函数和拷贝赋值运算符。
如何才能关闭这一特性呢?答案的关键是,所有编译器的产出函数都是public
。为了阻止这些函数,我们要得自行声明它们。因此,我们可以将它们声明为private
。这样,编译器就不再有理由为我们合成默认的函数了:因为我们已经自己提供了它们的声明。
另一个关键的技巧是声明它们,却不提供实现。因为成员函数和友元函数当中可能会利用实现来复制出多份对象的副本。这样一来,如果客户的代码尝试拷贝单例对象,则会导致链接过程出错,因为找不到对应的拷贝构造函数或是拷贝复制运算符的实现。
能否让这个错误出现在编译期间呢?答案是肯定的,只需要将这些用不到的函数抽取到一个父类当中:
class Uncopyable
{
protected:
Uncopyable() {}
~Uncopyable() {}
private:
Uncopyable(const Uncopyable &);
Uncopyable &operator=(const Uncopyable &);
};
这样,只要让单例类继承Uncopyable
即可。在编译时,编译器会试图为单例类生成默认的拷贝构造函数和拷贝赋值运算符,然而这些编译器的生成版本一定会尝试调用父类的同名函数,但是这些函数调用会被编译器拒绝,因为它们在父类中是private
的。
【注意】
为了避免编译器为我们生成额外的函数,可以在private
访问权限下手动生成这些函数,但是不提供实现;同时也可以使用类似于Uncopyable
这样的基类。