jpf*_*342 15 c++ copy-constructor assignment-operator copy-and-swap c++11
为了正确处理对象复制,经验法则是三规则.使用C++ 11,移动语义是一个东西,所以它是五条规则.然而,在这里和互联网上的讨论中,我也看到了对四条规则(一半)的引用,它是五条规则和复制交换习语的组合.
究竟是什么(四分之一)呢?需要实现哪些功能,每个功能的主体应该是什么样的?一半的功能是什么?与五法则相比,这种方法有任何缺点或警告吗?
这是一个类似于我当前代码的参考实现.如果这不正确,那么正确的实现会是什么样的?
//I understand that in this example, I could just use `std::unique_ptr`.
//Just assume it's a more complex resource.
#include <utility>
class Foo {
public:
//We must have a default constructor so we can swap during copy construction.
//It need not be useful, but it should be swappable and deconstructable.
//It can be private, if it's not truly a valid state for the object.
Foo() : resource(nullptr) {}
//Normal constructor, acquire resource
Foo(int value) : resource(new int(value)) {}
//Copy constructor
Foo(Foo const& other) {
//Copy the resource here.
resource = new int(*other.resource);
}
//Move constructor
//Delegates to default constructor to put us in safe state.
Foo(Foo&& other) : Foo() {
swap(other);
}
//Assignment
Foo& operator=(Foo other) {
swap(other);
return *this;
}
//Destructor
~Foo() {
//Free the resource here.
//We must handle the default state that can appear from the copy ctor.
//(The if is not technically needed here. `delete nullptr` is safe.)
if (resource != nullptr) delete resource;
}
//Swap
void swap(Foo& other) {
using std::swap;
//Swap the resource between instances here.
swap(resource, other.resource);
}
//Swap for ADL
friend void swap(Foo& left, Foo& right) {
left.swap(right);
}
private:
int* resource;
};
Run Code Online (Sandbox Code Playgroud)
Jar*_*d42 13
究竟是什么(四分之一)呢?
"四大(一半)规则"指出,如果你实施其中一个
那么你必须有一个关于其他人的政策.
需要实现哪些功能,每个功能的主体应该是什么样的?
移动构造函数(使用默认构造函数和交换):
S(S&& s) : S{} { swap(*this, s); }
Run Code Online (Sandbox Code Playgroud)赋值运算符(使用构造函数和交换)
S& operator=(S s) { swap(*this, s); }
Run Code Online (Sandbox Code Playgroud)析构函数(资源的深层副本)
std::swap使用move(或copy)构造函数,这将导致无限递归.一半的功能是什么?
从上一篇文章:
"为了实现Copy-Swap习惯用法,你的资源管理类还必须实现一个swap()函数来执行逐个成员的交换(这是你的"......(和一半)")
所以swap方法.
与五法则相比,这种方法有任何缺点或警告吗?
我已经写过的警告是要写正确的交换以避免无限递归.
与五法则相比,这种方法有什么缺点或警告吗?
虽然它可以节省代码重复,但使用复制和交换只会导致更糟糕的类,直言不讳。你正在损害你班级的表现,包括移动赋值(如果你使用统一赋值运算符,我也不喜欢它),这应该非常快。作为交换,您将获得强大的异常保证,这乍一看似乎不错。问题是,您可以使用简单的泛型函数从任何类中获得强大的异常保证:
template <class T>
void copy_and_swap(T& target, T source) {
using std::swap;
swap(target, std::move(source));
}
Run Code Online (Sandbox Code Playgroud)
就是这样。所以需要强异常安全的人无论如何都可以得到它。坦率地说,无论如何,强大的异常安全性是一个小众市场。
避免代码重复的真正方法是通过零规则:选择成员变量,这样您就不需要编写任何特殊函数。在现实生活中,我会说 90+% 的时间我看到特殊的成员函数,它们很容易被避免。即使您的类确实具有特殊成员函数所需的某种特殊逻辑,您通常最好将其推下成为会员。您的记录器类可能需要在其析构函数中刷新缓冲区,但这不是编写析构函数的理由:编写一个处理刷新的小缓冲区类并将其作为记录器的成员。记录器可能拥有各种可以自动处理的其他资源,您希望让编译器自动生成复制/移动/销毁代码。
关于 C++ 的事情是,特殊函数的自动生成对于每个函数来说要么全有要么全无。这就是拷贝构造函数(如)要么被自动生成的,同时考虑到所有成员,或者你必须写(更糟的是,保持),它的所有的手。所以它强烈地促使你采用一种向下推动事物的方法。
如果您正在编写一个类来管理资源并需要处理这个问题,它通常应该是:a) 相对较小,以及 b) 相对通用/可重用。前者意味着一点重复的代码没什么大不了的,后者意味着你可能不想把性能放在桌面上。
总之,我强烈反对使用复制和交换,以及使用统一赋值运算符。尝试遵循零规则,如果不能,遵循五规则。swap仅当您可以使其比通用交换(执行 3 次移动)更快时才写入,但通常您不必费心。
| 归档时间: |
|
| 查看次数: |
2120 次 |
| 最近记录: |