为什么复制赋值运算符必须返回引用/ const引用?

bks*_*bks 62 c++ operator-overloading copy-constructor assignment-operator

在C++中,我不清楚从复制赋值运算符返回引用的概念.为什么复制赋值运算符不能返回新对象的副本?另外,如果我上课A,还有以下内容:

A a1(param);
A a2 = a1;
A a3;

a3 = a2; //<--- this is the problematic line
Run Code Online (Sandbox Code Playgroud)

operator=定义如下:

A A::operator=(const A& a)
{
    if (this == &a)
    {
        return *this;
    }
    param = a.param;
    return *this;
}
Run Code Online (Sandbox Code Playgroud)

Mic*_*urr 66

严格地说,复制赋值运算符的结果不需要返回引用,但是为了模仿C++编译器使用的默认行为,它应该返回对分配给的对象的非const引用(隐式生成的副本)赋值运算符将返回非const引用 - C++ 03:12.8/10).我已经看到了一些void从复制赋值重载返回的代码,我不记得何时引起严重问题.返回void将阻止用户进行"分配链接"(a = b = c;例如,将阻止在测试表达式中使用赋值结果.虽然这种代码绝不是闻所未闻,但我也认为它并不常见 - 特别是对于非原始类型(除非类的接口打算进行这类测试,例如iostreams).

我不是建议你这样做,只是指出它是允许的,它似乎并没有引起很多问题.

这些其他SO问题与您可能感兴趣的信息/意见相关(可能不是完全愚蠢).


小智 55

稍微澄清为什么最好通过引用operator=返回以及按值返回---因为a = b = c如果返回值,链将正常工作.

如果您返回参考,则完成最少的工作.来自一个对象的值将复制到另一个对象.

但是,如果按值返回operator=,则会调用构造函数和析构函数每次调用赋值运算符!!

所以,给定:

A& operator=(const A& rhs) { /* ... */ };
Run Code Online (Sandbox Code Playgroud)

然后,

a = b = c; // calls assignment operator above twice. Nice and simple.
Run Code Online (Sandbox Code Playgroud)

但,

A operator=(const A& rhs) { /* ... */ };

a = b = c; // calls assignment operator twice, calls copy constructor twice, calls destructor type to delete the temporary values! Very wasteful and nothing gained!
Run Code Online (Sandbox Code Playgroud)

总而言之,通过价值回归没有任何好处,但要失去很多.

(注意:这并不是为了解决赋值运算符返回左值的优点.请阅读其他帖子,了解为什么这可能更合适)


Jer*_*fin 9

当你重载时operator=,你可以写它来返回你想要的任何类型.如果你想要足够严重,你可以重载X::operator=以返回(例如)一些完全不同的类的实例YZ.但这通常是非常不可取的.

特别是,您通常希望operator=像C一样支持链接.例如:

int x, y, z;

x = y = z = 0;
Run Code Online (Sandbox Code Playgroud)

在这种情况下,您通常希望返回所分配类型的左值或右值.这只留下了是否返回对X的引用,对X的const引用或X(按值)的问题.

将const引用返回到X通常是一个糟糕的想法.特别是,允许​​const引用绑定到临时对象.临时的生命周期延长到它所绑定的引用的生命周期 - 但不会递归到可能分配给它的任何生命周期.这使得返回悬空引用变得容易 - const引用绑定到临时对象.该对象的生命周期延长到引用的生命周期(在函数结束时结束).到函数返回时,引用和临时的生命周期已经结束,因此分配的是悬空引用.

当然,返回非const引用并不能提供完全的保护,但至少会让你更加努力.您仍然可以(例如)定义一些本地,并返回对它的引用(但大多数编译器可以并且也将对此发出警告).

返回值而不是引用具有理论和实际问题.在理论方面,=在这种情况下,通常意味着它意味着什么.特别是,在赋值通常意味着"获取此现有源并将其值分配给此现有目标"时,它开始意味着更像"获取此现有源,创建它的副本,并将该值分配给此现有目标. "

从实用的角度来看,尤其是在发明右值引用之前,这可能会对性能产生重大影响 - 在复制A到B的过程中创建一个完整的新对象是出乎意料的并且通常很慢.例如,如果我有一个小向量,并将其分配给一个更大的向量,我希望最多花时间复制小向量的元素加上(小)固定开销来调整大小目的地矢量.如果它涉及两个副本,一个从源到临时,另一个从临时到目的地,并且(更糟)动态分配临时向量,我对操作复杂性的期望将完全被破坏.对于小向量,动态分配的时间可能比复制元素的时间高很多倍.

唯一的另一个选项(在C++ 11中添加)将返回一个右值引用.这很容易导致意想不到的结果 - 链接的任务a=b=c;可能会破坏内容b和/或c,这将是非常意外的.

这使得返回正常引用(不是对const的引用,也不是rvalue引用)作为(合理地)可靠地产生大多数人通常想要的唯一选项.


Pup*_*ppy 5

部分原因是返回对self的引用比按值返回更快,但此外,它允许原始类型中存在的原始语义.

  • 你相信,你会得到(a =(b = c)),这仍然会产生预期的结果.只有你这样做(a = b)= c才会被打破. (4认同)