noncopyable
noncopyable 允许程序轻松地实现一个禁止拷贝的类。
noncopyable位于名字空间boost,为了使用noncopyable 组件,需要包含头文件<boost/noncopyable.hpp>或者<boost/utility.hpp>(后者包含了数个小工具的实现):
#include <boost/noncopyable.hpp> //或者
#include <boost/utility.hpp>
原理
在C++中定义一个类时,如果不明确定义拷贝构造函数和拷贝赋值操作符,编译器会为我们自动生成这两个函数。
例如:
class empty_class();
这样一个简单的“空”类R,编译器在处理时会“默默地”为它增加拷贝构造函数和拷贝赋值操作符,真实代码类似于:
class empty_class
{
public: //构造和析构暂时忽略
empty_class (const empty_class &){
...} //拷贝构造函数
empty_class & operator=(const empty_class &){
...} //拷贝赋值
};
一般情况下这是有用的,比如可以自动支持swap()、符合容器的拷贝语义、可以放入标准容器处理,但有的时候我们不需要类的拷贝语义,希望禁止拷贝类的实例。
这是一个很经典的c++惯用法,原理很好理解,只需要私有化拷贝构造函数和拷贝赋值操作符即可,手写代码也很简单(scoped_ptr就使用了这个惯用法),例如:
class do_not_copy
{
private:
do_not_copy (const do_not_copy &); //私有化,声明即可,不需要实现代码
void operator=(const do_not_copy &); //私有化,声明即可,不需要实现代码
};
但如果程序中有大量这样的类,重复写这样的代码是相当乏味的,而且代码出现的次数越多越容易增大手写出错的几率。虽然也可以用带参数的宏来减少重复,但解决方案不够优雅。
用法
noncopyable为实现不可拷贝的类提供了简单清晰的解决方案:从boost::noncopyable派生即可。
使用noncopyable,上面的例子可简化为:
#include <boost/noncopyable.hpp>
class do_not_copy: boost::noncopyable
{
...};
注意,这里使用缺省的私有继承是允许的。我们也可以显式写出 private或者public修饰词,但效果是相同的。因此直接这样写少输入了一些代码,也更清晰,并且表明了HAS-A关系(而不是工S-A)。
如果有其他人误写了代码(很可能是没有仔细阅读接口文档),企图拷贝构造或者赋值do_not_copy,那么将不能通过编译器的审查:
do_not_copy d1; //一个不可拷贝对象
do_not_copy d2(d1); //企图拷贝构造,编译出错
do_not_copy d3; //另一个不可拷贝对象
d3 = d1; //企图拷贝赋值,编译出错
使用GCC编译会报出类似下面的错误提示:
note: 'do_not_copy::do_not_copy(const do_not_copy&)'
is implicitly deleted because the default definition would be ill-formed:
class do_not_copy: boost::noncopyable
error: use of deleted function 'boost::noncopyable_ ...'
这条错误信息明确地告诉我们:类使用boost::noncopyable禁用(delete)了拷贝构造,无法调用拷贝构造函数。
只要有可能,就使用boost::noncopyable,它明确无误地表达了类设计者的意图,对用户更加友好,而且与其他Boost库也配合得很好。
实现
class noncopyable
{
protected:
noncopyable() {
}
~noncopyable() {
}
private:
noncopyable (const noncopyable&); //私有化拷贝构造和拷贝赋值
const noncopyable& operator=(const noncopyable&);
};
因此,当我们的自定义类是noncopyable的子类时就会自动私有化父类noncopyable的拷贝构造函数,从而禁止用户从外部访问拷贝构造函数和拷贝赋值函数。
如果使用C++11标准的新default和 delete关键字,则noncopyable可以更清晰地实现如下:
class noncopyable
{
protected:
noncopyable() = default; //默认的构造和析构是保护的
~noncopyable() = default; //使用默认实现
//使用delete关键字禁用拷贝构造和拷贝赋值
noncopyable(const noncopyable&) = delete;
const noncopyable& operator=(const noncopyable&) = delete;
};
代码示例
#include <iostream>
using namespace std;
#include <boost/noncopyable.hpp>
//
class do_not_copy : boost::noncopyable
{
};
//
int main()
{
do_not_copy d1;
//do_not_copy d2(d1);
//do_not_copy d3;
//d3 = d1;
}