在复制赋值运算符中按值传递与按引用传递

Pass by value vs. pass by reference in copy assignment operator

提问人:24n8 提问时间:2/28/2020 最后编辑:xskxzr24n8 更新时间:2/28/2020 访问量:514

问:

首先,有一个类似的热门帖子 什么是复制和交换成语?. 接受的答案有一个指向 https://web.archive.org/web/20140113221447/http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/ 的链接。

接受的页面和链接的页面都声明复制分配运算符的常用实现是(从上一个链接复制和粘贴)

T& T::operator=(T const& x) // x is a reference to the source
{ 
    T tmp(x);          // copy construction of tmp does the hard work
    swap(*this, tmp);  // trade our resources for tmp's
    return *this;      // our (old) resources get destroyed with tmp 
}

但是那

T& operator=(T x)    // x is a copy of the source; hard work already done
{
    swap(*this, x);  // trade our resources for x's
    return *this;    // our (old) resources get destroyed with x
}

由于编译器的复制省略优化,或者通常,始终按值传递而不是通过引用传递,然后复制通过引用传递的参数,因此效果更好。

我同意第二种选择与第一种相同或更好,但并不差,但我感到困惑的是,为什么第一种选择一开始就是这样写的。我不明白为什么需要临时变量和交换。

相反,我们不能做这样的事情吗:

T& T::operator=(T const& x) // x is a reference to the source
{ 
    this->member_var = x.member_var;
    //if we have to do a deep copy of something, implement that here
    return *this;
}

它不使用复制构造函数。

C++ 复制分配 和交换

评论

1赞 François Andrieux 2/28/2020
@JerryJeremiah 这完全取决于复制构造函数的实现方式......复制/交换不会强制浅层复制。
2赞 François Andrieux 2/28/2020
您对我们使用复制/交换的原因及其成本是正确的。复制/交换不是必需的,它不会自动成为更好的解决方案。但它非常有用。您可能从链接的问题中错过了一个好处,那就是异常安全性。使用复制/交换时,分配要么成功,要么保持不变。 应始终如此,只有副本才能失败。如果是这样,则永远不会发生。这样做几乎总是会有一些开销,而且使用复制/交换的开销并没有太大区别。thisswapnoexceptswap
1赞 François Andrieux 2/28/2020
当你调用时,如果你传递的对象是右值,则参数将被移动构造。例如,in in 参数是从 中构造的 move 。然后将与 .但是,在这种情况下,右值永远不会只生成右值引用。operator=xfoo = std::move(bar)foo::operator=(foo x)xbarx*thisxstd::move(bar)
1赞 François Andrieux 2/28/2020
x是左值表达式,与任何其他命名变量一样。它有一个名称 (),你可以取它的地址 ()。它的构造方式不会改变它的价值类别。x&x
1赞 François Andrieux 2/28/2020
诀窍在于,值类别适用于表达式,而不是它们包含或表示的值。如果你有,那么是一个左值。如果有,则调用函数的结果是右值。如果这样做了,那么仍然返回一个右值,并且仍然是一个左值,即使它们现在表示相同的状态。T x;xT function();x = function();function()x

答:

2赞 xskxzr 2/28/2020 #1

如果有多个成员,则赋值运算符不是异常安全的:

T& T::operator=(T const& x) 
{ 
    this->member_var1 = x.member_var1; 
    this->member_var2 = x.member_var2; // if an exception occurs here, this->member_var1 will still be changed
    return *this;
}

评论

2赞 ShadowRanger 2/28/2020
这与线程安全无关(如果没有同步,多成员交换也不是神奇的“安全”;几乎任何在没有同步或原子的情况下读取数据的尝试都可能读取垃圾,尤其是在具有弱有序内存模型的系统上,或者由于撕裂),而是关于强大的异常保证。