一般来说,管理资源应该是没有,可复制或有专门的拷贝语义的任何类。反过来也是如此:任何不可复制或需要特殊复制语义的类都是管理资源。在实践中,C++语言中的“管理资源”意味着负责内存中的某些空间,或者连接到网络或数据库,或者文件句柄或撤销事务等等。
资源管理捕获了很多例子。这些职责是采取前缀操作,后缀操作以及可能的一些操作。例如,内存管理涉及获取我们将要管理的内存地址的句柄,可能与该内存混乱,并最终释放句柄(因为如果你喜欢某事,就让它免费)。
template<typename T>
struct memory {
memory(T const& val = T()) : p(new T(val)) { }
~memory() { delete p }
T& operator*() const { return *p; }
private:
T* p;
};
// ...
{
memory<int> m0;
*m0 = 3;
std::cout << *m0 << '\n';
}
这memory
类几乎是正确的:它会自动获取底层的内存空间,并自动将其释放,即使异常传播一段时间后,它收购了其资源。但考虑这样的场景:
{
memory<double> m1(3.14);
memory<double> m2(m1); // m2.p == m1.p (do you hear the bomb ticking?)
}
因为我们没有为memory
提供专门的复制语义中,编译器提供了它自己的拷贝构造函数和拷贝赋值。这些做错误事情:m2 = m1
意味着m2.p = m1.p
,使这两个指针指向相同的地址。这是错误的,因为当m2
超出范围时,它将其资源释放为一个好的责任对象,当m1
超出范围时,它也释放其资源,同一资源m2
已经释放,完成双重删除 - 臭名昭着未定义的行为场景。而且,在C++中,创建对象的副本是非常容易的,甚至不会注意到:函数根据值取其参数,按值返回它的参数,或者通过引用取其参数,然后调用另一个函数,该函数本身需要(或返回)参数值...更容易假设事情将试图得到复制。
这一切都是说,当一个班级的理由是管理资源时,你应该立即知道你需要处理复制。你应该决定
- 你支持复制,而你决定的意思复制:资源的安全共享,进行底层资源的深层副本所以没有共享任何或这两种方法在copy-on-write合并或懒惰的副本。无论您选择哪种路径,您都需要提供专门的复制构造函数和复制赋值运算符。
- 或者您不支持任何形式的资源复制,在这种情况下您将禁用复制构造函数和复制赋值运算符。
我会走到目前为止,并说资源管理是唯一的情况下,您禁用复制或提供专门的复制语义。这仅仅是The Rule of Three的另一个角度。
一个单就是一个例子。 – 2012-01-29 20:09:15