在C ++中,从拷贝赋值运算符返回引用的概念,我不清楚。 为什么不能拷贝赋值运算符返回新对象的副本? 另外,如果我有一流的A
,并执行以下操作:
A a1(param);
A a2 = a1;
A a3;
a3 = a2; //<--- this is the problematic line
的operator=
定义如下:
A A::operator=(const A& a)
{
if (this == &a)
{
return *this;
}
param = a.param;
return *this;
}
严格地说,一个拷贝赋值运算符的结果并不需要返回一个参考,但要模仿C ++编译器使用默认的行为,它应该返回非const引用分配给(隐式生成的拷贝对象赋值运算符将返回一个非const引用 - C ++ 03:12.8 / 10)。 我见过的代码公平一点,返回void
的拷贝赋值过载,我不记得那个时候造成了严重的问题。 返回void
将防止“分配链接”(用户a = b = c;
并且将防止使用在测试表达的分配的结果,例如。 虽然那种代码绝非闻所未闻的,我也不认为这是特别常见的 - 尤其是对非原始类型(除非一类接口打算为这些类型的测试,如输入输出流)。
我不建议您这样做,只是指出,它的许可,并且它似乎并没有引起一大堆的问题。
这些其他的SO问题是相关的(可能不太愚弄)具有信息/意见,可能是你的兴趣。
- 有没有人发现需要声明一个拷贝赋值运算符常量的返回参数?
- 重载赋值运算符在C ++
澄清的位,为什么它优选通过参考用于向返回operator=
与由值返回---作为链a = b = c
将正常工作,如果被返回的值。
如果你返回一个引用,最少的工作就完成了。 从一个对象中的值被复制到另一个对象。
但是,如果通过值返回operator=
,你会调用构造函数和析构函数每一次的赋值运算符被称为!
因此,考虑到:
A& operator=(const A& rhs) { /* ... */ };
然后,
a = b = c; // calls assignment operator above twice. Nice and simple.
但,
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!
总之,没有什么通过返回值获得,但失去的东西很多。
( 注 :这并不意味着,以解决其赋值操作符返回左值的优势阅读其他职位的原因,可能是可取的。)
当重载operator=
,你可以写它返回任何你想要的类型。 如果您想不好的话,你可以重载X::operator=
返回(例如)一些完全不同的类的实例Y
或Z
。 这通常是极不妥当,但。
特别是,你通常要支持的链接operator=
就像C语言一样。 例如:
int x, y, z;
x = y = z = 0;
既然如此,你通常要返回类型的左值或右值分配给。 这只是叶是否参考返回X,const引用到X,或X(按价值计算)的问题。
返回一个常量引用X通常是一个贫穷的想法。 特别是,const引用被允许结合于临时对象。 临时的寿命延长至到它绑定参考的一生 - 但不是递归到任何的可能被分配到寿命。 这可以很容易地返回悬空参考 - 常量引用结合到一个临时对象。 该对象的寿命延长到参考的寿命(其在函数结束时结束)。 该函数返回时,参考的使用寿命,并暂时已经结束的时候,有啥分配是悬空的参考。
当然,返回非const引用不提供对这种完全的保护,但至少让你的工作在这有点困难。 您仍然可以(例如)定义了一些地方,并返回对它的引用(但大多数编译器能够并且将会警告这太)。
返回一个值,而不是一个参考具有重要的理论和实际问题。 在理论方面,您有以下两种基本脱节=
通常意味着这意味着什么在这种情况下。 特别是,在分配通常是指“利用这个现有的源,并将其值分配到这个现有的目的地”,它开始意味着更多的东西,如“利用这个现有的源,创建一个副本,并将该值分配到这个现有的目的地。 “
从实用的角度来看,发明了右值引用尤其之前,这可能对性能有显著的影响 - 在创建复制的过程中一个全新的对象到B是意外的,往往相当缓慢。 如果,例如,我有一个小的矢量,并将其分配到一个较大的载体,我期望拿,最多一次复制小矢量的元素和一个(小)的固定开销来调整大小目的载体。 如果不是涉及到两个副本,一个从源头到温度,另一个从温度到目的地,和(差)动态分配的临时矢量,我对操作的复杂性预期将完全摧毁。 对于一个小矢量,对于动态分配的时间可以很容易地比复制元素时高许多倍。
唯一的其他选择(在C ++ 11加入)将返回一个rvalue参考。 这很容易导致意外的结果-一个链式分配如a=b=c;
可以摧毁的内容b
和/或c
,这将是相当意外。
这使得恢复正常参考值(不是引用到常量,也不是一个右值引用)作为唯一的选项(合理的)可靠地生产的大多数人通常想要的。
这部分是因为返回参考自我是不是按值返回更快,但除此之外,它是允许存在于原始类型的原始语义。
operator=
可以被定义为返回任何你想要的。 你需要更具体的,以什么问题实际上是; 我怀疑你有拷贝构造函数使用operator=
内部并导致堆栈溢出,因为拷贝构造函数调用operator=
必须使用拷贝构造函数返回A
由值循环往复。
存在对结果类型用户定义的无核心语言要求operator=
,但标准库确实有这样的要求:
C ++ 98§23.1/ 3:
”类型存储在这些部件的对象必须满足的要求CopyConstructible
类型(20.1.3),和的附加要求Assignable
的类型。
C ++ 98§23.1/ 4:
”以表64, T
是用于实例化容器的类型, t
是的值T
,和u
是(可能的值const
) T
。
通过返回值的副本仍然会支持分配链接像a = b = c = 42;
,因为赋值运算符是右结合的,即,该被解析为a = (b = (c = 42));
。 但是返回的副本将禁止无意义构造像(a = b) = 666;
。 对于一个小的类返回一个拷贝可以令人信服地最有效的,而对于较大的类通过引用返回一般将是最有效的(和复制,过于低效)。
直到我了解了标准库的要求,我习惯让operator=
返回void
,以提高效率和避免的配套副作用基于糟糕的代码是愚蠢的。
用C ++ 11有另外的要求T&
结果类型为default
-ing赋值运算符,因为
C ++ 11§8.4.2/ 1:
”被明确地默认函数应当[...]具有相同的声明的函数类型(除了可能不同REF-限定符和不同之处在于在一个拷贝构造的情况下,或复制赋值运算符,参数类型可以是“参照非常量T
”,其中T
是成员函数的类的名称),就好像它已经隐含声明