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