如何为复制交换习语实现复制构造函数
How to implement copy constructor for copy swap idiom
我正在尝试为 class 实现复制构造函数和赋值运算符。我对复制交换习语有点困惑。特别是当涉及到复制构造函数时。复制交换习语与复制构造函数有任何关系吗?如何避免代码重复?
这是我的class
Header:
Class Actor
{
public:
Foo* foo;
Bar bar;
double member1;
bool member2;
unsigned int member3;
void Swap(Actor& first, Actor& second);
Actor(const Actor&);
Actor& operator=(const Actor);
}
Cpp:
void Actor::Swap(Actor& first, Actor& second)
{
// Swap wont work with my non pointer class
Bar temp = first.bar;
first.bar = second.bar;
second.bar = temp;
std::swap(first.foo, second.foo);
std::swap(first.member2, second.member2);
std::swap(first.member3, second.member3);
}
// What goes here? Is this a correct copy constructor? Does this have anything to do with a copy swap idiom? How can I avoid code duplication in my copy constructor?
Actor::Actor(const Actor& other)
{
foo = new Foo();
*foo = *other.foo;
bar = other.bar;
member1 = other.member1;
member2 = other.member2;
member3 = other.member3;
}
Actor& Actor::operator=(Actor other)
{
Swap(*this, other);
return *this;
}
我正在遵循此指南:What is the copy-and-swap idiom?
复制交换方法是一种在重用交换和复制构造函数方面实现复制赋值运算符的方法,以减少重复代码并增加异常安全性。
那就是说我注意到了您的代码:
- 您没有析构函数,因此当您的
Actor
被销毁时,您将泄漏 Foo* foo;
对象。
- 在您的复制构造函数中,您可以一步分配和复制
Foo
,而不需要您分配给的中间默认构造 Foo
。
- 您的
Swap
函数是一个有两个参数的成员,这实际上意味着它有三个参数。它应该是单参数成员并与 this
或双参数非成员交换。
- 为了与标准库保持一致,我建议调用交换函数
swap
而不是 Swap
,无论您采用哪种实现方法。
- 在你的交换中,更喜欢删除
Bar
交换到 Bar
而不是在 Actor
class 中实现它:换句话说让 Bar
知道如何交换自身并简单地利用该功能。
copy-and-swap assignment 习语需要两种机制:copy c'tor 和 non-throwing swap function。
所以你的代码完全按照它来写。
想法是首先尝试执行不安全的操作,只需定义一个新对象即可创建一个副本。如果它失败并抛出,您分配给的对象保持不变,它的状态在发生错误时是可预测的。那就是异常安全保证。
如果复制时没有出现错误,则进行交换以完成分配。由于 swap 方法是非抛出的,因此您没有让分配给对象处于不一致状态的风险。
最后,临时对象的析构函数清除了所有留下的资源,因为交换方法将这些资源交给了它。
我认为这个实现很好,但有几点除外:
1) 您忘记在自定义 Swap
方法中交换 member1
。
2) Copy-and-swap 习语通常为operator=
提供强大的异常保证。这意味着它要么在不更改对象的情况下因异常而失败,要么成功执行赋值。在您的情况下,强异常安全性值得怀疑,因为 Bar
赋值可能会引发异常。
How do I avoid code duplication?
说实话,复制和交换 (CAS) 背后的主要动机不是避免代码重复,而是提供强大的异常保证。这意味着如果您尝试执行复制赋值但失败了,那么您尝试赋值的对象不会以任何方式被修改。一般来说,moveconstruction/assignment不抛出,而复制构造隐含地给出了这个保证,因为如果抛出异常,对象将不存在。所以在典型情况下,复制赋值是 "odd man out" 不提供强异常保证或更好。
也就是说,强大的异常保证通常很难提供,并且在实践中可能没有那么有用。我不会鼓励人们自动假设 CAS 是最好的东西。
如果您的目标是避免代码重复,最好的方法是使用零规则:http://en.cppreference.com/w/cpp/language/rule_of_three。基本上,想法是为您的 class 选择个别成员,以便编译器生成 copy/move constructor/assignment 是您想要的行为。我知道这段代码可能是为了理解这些功能而进行的练习,但是当您为了自己而不是为了学习而编写代码时,最好意识到这一点。
编辑:最后一点。假设您使用的是 C++11,一般来说,使用 CAS 的推荐方法实际上 而不是 是自己编写交换。相反,您将编写移动构造函数(无论如何您都必须这样做),然后移动赋值。从这两个函数中,泛型 std::swap
将能够有效地交换你的 class。然后您可以在 CAS 实现中使用通用 swap
。在某些情况下,您可能会自己编写交换,但通常没有必要。更详细的讨论在这里:http://scottmeyers.blogspot.com/2014/06/the-drawbacks-of-implementing-move.html.
我正在尝试为 class 实现复制构造函数和赋值运算符。我对复制交换习语有点困惑。特别是当涉及到复制构造函数时。复制交换习语与复制构造函数有任何关系吗?如何避免代码重复?
这是我的class
Header:
Class Actor
{
public:
Foo* foo;
Bar bar;
double member1;
bool member2;
unsigned int member3;
void Swap(Actor& first, Actor& second);
Actor(const Actor&);
Actor& operator=(const Actor);
}
Cpp:
void Actor::Swap(Actor& first, Actor& second)
{
// Swap wont work with my non pointer class
Bar temp = first.bar;
first.bar = second.bar;
second.bar = temp;
std::swap(first.foo, second.foo);
std::swap(first.member2, second.member2);
std::swap(first.member3, second.member3);
}
// What goes here? Is this a correct copy constructor? Does this have anything to do with a copy swap idiom? How can I avoid code duplication in my copy constructor?
Actor::Actor(const Actor& other)
{
foo = new Foo();
*foo = *other.foo;
bar = other.bar;
member1 = other.member1;
member2 = other.member2;
member3 = other.member3;
}
Actor& Actor::operator=(Actor other)
{
Swap(*this, other);
return *this;
}
我正在遵循此指南:What is the copy-and-swap idiom?
复制交换方法是一种在重用交换和复制构造函数方面实现复制赋值运算符的方法,以减少重复代码并增加异常安全性。
那就是说我注意到了您的代码:
- 您没有析构函数,因此当您的
Actor
被销毁时,您将泄漏Foo* foo;
对象。 - 在您的复制构造函数中,您可以一步分配和复制
Foo
,而不需要您分配给的中间默认构造Foo
。 - 您的
Swap
函数是一个有两个参数的成员,这实际上意味着它有三个参数。它应该是单参数成员并与this
或双参数非成员交换。 - 为了与标准库保持一致,我建议调用交换函数
swap
而不是Swap
,无论您采用哪种实现方法。 - 在你的交换中,更喜欢删除
Bar
交换到Bar
而不是在Actor
class 中实现它:换句话说让Bar
知道如何交换自身并简单地利用该功能。
copy-and-swap assignment 习语需要两种机制:copy c'tor 和 non-throwing swap function。
所以你的代码完全按照它来写。
想法是首先尝试执行不安全的操作,只需定义一个新对象即可创建一个副本。如果它失败并抛出,您分配给的对象保持不变,它的状态在发生错误时是可预测的。那就是异常安全保证。
如果复制时没有出现错误,则进行交换以完成分配。由于 swap 方法是非抛出的,因此您没有让分配给对象处于不一致状态的风险。
最后,临时对象的析构函数清除了所有留下的资源,因为交换方法将这些资源交给了它。
我认为这个实现很好,但有几点除外:
1) 您忘记在自定义 Swap
方法中交换 member1
。
2) Copy-and-swap 习语通常为operator=
提供强大的异常保证。这意味着它要么在不更改对象的情况下因异常而失败,要么成功执行赋值。在您的情况下,强异常安全性值得怀疑,因为 Bar
赋值可能会引发异常。
How do I avoid code duplication?
说实话,复制和交换 (CAS) 背后的主要动机不是避免代码重复,而是提供强大的异常保证。这意味着如果您尝试执行复制赋值但失败了,那么您尝试赋值的对象不会以任何方式被修改。一般来说,moveconstruction/assignment不抛出,而复制构造隐含地给出了这个保证,因为如果抛出异常,对象将不存在。所以在典型情况下,复制赋值是 "odd man out" 不提供强异常保证或更好。
也就是说,强大的异常保证通常很难提供,并且在实践中可能没有那么有用。我不会鼓励人们自动假设 CAS 是最好的东西。
如果您的目标是避免代码重复,最好的方法是使用零规则:http://en.cppreference.com/w/cpp/language/rule_of_three。基本上,想法是为您的 class 选择个别成员,以便编译器生成 copy/move constructor/assignment 是您想要的行为。我知道这段代码可能是为了理解这些功能而进行的练习,但是当您为了自己而不是为了学习而编写代码时,最好意识到这一点。
编辑:最后一点。假设您使用的是 C++11,一般来说,使用 CAS 的推荐方法实际上 而不是 是自己编写交换。相反,您将编写移动构造函数(无论如何您都必须这样做),然后移动赋值。从这两个函数中,泛型 std::swap
将能够有效地交换你的 class。然后您可以在 CAS 实现中使用通用 swap
。在某些情况下,您可能会自己编写交换,但通常没有必要。更详细的讨论在这里:http://scottmeyers.blogspot.com/2014/06/the-drawbacks-of-implementing-move.html.