通常,如果你不需要class提供某种机制,你只要不声明对应的函数就OK了。但拷贝构造和拷贝复制对这种做法具有免疫力。即如果你不声明拷贝构造和拷贝赋值,编译器也会高兴的帮你实现他们,而且它还是一个“活雷锋”,帮你实现了也不通知你。虽然编译器的这种策略,在大多数情况下是合理的,但是在某些情况下,我们是不需要这个“活雷锋”的。
我们被编译器逼到了一个困境,如果你不声明拷贝构造或拷贝赋值,编译会默认为你产生一份,于是你的class就支持bit拷贝了,但是如果你自己声明了拷贝构造或拷贝赋值呢?你的class还是没有逃过支持bit拷贝的命运。问题是如何才能真正避免拷贝构造和拷贝赋值呢?
如何避免拷贝构造和拷贝赋值,看下面的几种方法。
(1)所有编译器产出的函数都是public。为阻止这些函数被创建出来,你得自行声明它们,但这里并没有什么需求使你必须将它们声明为public 。可将copy构造函数或copy赋值操作符声明为private。采用明确声明一个成员函数,可阻止编译器暗自创建其专属版本;而令这些函数为private ,使你得以成功阻止人们调用它。
(2)一般而言这个做法并不绝对安全,因为member 函数和friend 函数还是可以调用你的private 函数。除非你够聪明,不去定义他们,即:只声明,不定义。
我们看下面避免拷贝构造和拷贝赋值的例子:
class User
{
private:
User(const User&); //只有声明,不实现
User& operator=(const User&);
}
或许你注意到了,我没写函数参数的名称。晤,参数名称并非必要,只不过大家总是习惯写出来。这个函数毕竟不会被实现出来,也很少被使用,指定参数名称又有何用?有了上述class 定义,当客户企图拷贝User对象,编译器会阻挠他。如果你不慎在member 函数或friend 函数之内那么做,轮到连接器发出抱怨。
此外,将连接期间发生的错误提升到编译期间也是可能的。而且这还是已经好事。实现这以目标也很简单。你只需要声明private,而不实现就可以了。所以如果你想拒绝编译器偷偷为你生成的函数,这种方法才是最好的方法。因为使用这种方法,你不当可以拒绝编译器默认生成的函数,还可以拒绝客户的调用。而上种方法将函数声明为private属性,同时给予实现。客户依然是可以调用的。看下面禁止任何形式的拷贝的例子:
// 不允许拷贝构造的类实现。
class Uncopy
{
public:
//允许derived对象构造和析构
Uncopy() {}
virtual ~Uncopy(} {}
private:
Uncopy(const Uncopy&}; //禁止拷贝
Uncopy& operator=(const Uncopy&);
};
这是行得通的,任何客户,甚至是member函数或friend函数,只要尝试拷贝Uncopy对象。就会调用Uncopy的拷贝构造函数。但在会被编译器所拒绝。因为Uncopy的拷贝构造函数没有函数实现。这是编译器无法容忍的。
将类的拷贝构造和拷贝赋值声明为private,可禁止对象拷贝。除此之外,如果将构造函数声明为private还可实现另外的功能,这就是控制对象生成的实例数。鼎鼎大名的单例模式就是采用这种机制实现的。我们看一个单例模式的例子:
class CSingleton
{
public:
virtual ~CSingleton() {}
static CSingleton* GetInstance()
{
static CSingleton instance;
return &instance;
}
private:
CSingleton(){}
};
因为构造函数是private的,我们不能为CSingleton类定义实例,而只能通过static的GetInstance函数接口生成此类的实例。由于GetInstance接口采用static属性,导致生成实例只能为一个。所以最终也就实现类对象单一性的控制了。
请谨记
- 为禁止编译器自动提供的机制,可将相应的成员函数声明为private并不予以实现。
- 将类的构造函数声明为private,通过一个static的函数接口实现类的使用。这样可以保证类对象实例的单一性。