为什么在传递重载运算符的返回值时不调用复制构造函数

时间:2016-05-20 05:50:18

标签: c++ copy-constructor

考虑以下情况

class Integer
{
   long long n;
   public:
   Integer(long long i):n(i){}
   Integer(){cout<<"constructor";}
   void print()
   {
      cout<<n<<endl;
   }
   Integer(const Integer &a){cout<<"copy constructor"<<" "<<a.n<<endl;}
   Integer operator+(Integer b);
};

Integer Integer:: operator+(Integer b)
{
   this->n = this->n + b.n;
   return *this;
}
int main() 
{
   // your code goes here
   Integer a(5);
   Integer b(6);
   Integer c(a+b);//line 1
   return 0;
 }

如果a+b是临时的,那么我理解不会调用复制构造函数。但a+b不会返回临时。 我得到的输出是

 copy constructor 6  //this is because Integer object is passed as value to operator+
 copy constructor -5232903157125162015 //this is when object is returned by value from operator+

我认为在使用a+b初始化c时应该再打一次电话。大多数相关问题与返回值优化有关,但i无法将RVO与此相关联。

4 个答案:

答案 0 :(得分:2)

您说 a+b不会返回临时。错误。 a+b(即使它更改a肯定会误导未来的读者......)会返回a的临时副本,因为它被声明为返回Integer而不是引用(Integer&)。

所以这是发生的事情:

Integer a(5); // creates an integer from int 5
Integer b(6); // creates an integer from int 6

Integer c(a+b); /* first creates a temp using copy ctor from b
                   updates a
                   creates a temp copy of the result
                   should create an integer by copy of the result (elided)
                   destroys the temporary created from b
*/

return 0; // destroys c, b, a

BTW:您忘记在副本中初始化n - 它应该是:

Integer(const Integer &a):n(a.n) {cout<<"copy constructor"<<" "<<a.n<<endl;}

答案 1 :(得分:1)

您正在见证构造函数的copy ellission

  

当无名的临时(未绑定到任何引用)将被移动或复制到相同类型的对象(忽略顶级cv资格)时,将省略复制/移动。构造临时值时,它将直接构建在存储器中,否则将被移动或复制到该存储器中。当无名临时是return语句的参数时,copy elision的这个变体称为RVO,“返回值优化”。

要查看预期的输出(不要忽略构造函数),请使用-fno-elide-constructors中的gcc选项。

修改

正如我对你的问题Integer:: operator+的评论中所提到的那样。此外,如其他答案中所述,您的复制构造函数不会对成员进行初始化,从而导致未定义的行为。

答案 2 :(得分:1)

要回答您的问题,复制构造函数永远不会复制任何内容,X.GridPanel() .Cls("x-grid-custom twelve") .ID("gridIRPAlerts") .Scroll(ScrollMode.Both) .AutoScroll(true) .MaxHeight(240) .EnableColumnHide(false) .ColumnLines(true) .Store( X.Store() .RemoteFilter(true) .Model( X.Model() .Fields( // Approx 15 ModelFields... ) ) .Proxy( Html.X().AjaxProxy() .Url(Url.Action("GetAllIRPConfirmData", "CommonWidget")).Timeout(500000) //added timeout as record count is too big .ActionMethods(actions => { actions.Read = HttpMethod.POST; }) .Reader(Html.X().JsonReader().Root("data")) .Listeners(l => l.Exception.Handler = "Error()") ) ) .EmptyText("No IRP Alerts Available.") .ColumnModel( X.Column().DataIndex("Desc").ID("IRP_IDForHeader").Text("IRP").Flex(5), X.Column().DataIndex("Channel").Text("Channel").Flex(2), X.Column().DataIndex("PType").Text("My column").Flex(2), X.NumberColumn().DataIndex("Price").Text("New Price").Format("0.00").Flex(2).Align(Alignment.Right), X.DateColumn().DataIndex("Effective_Date").Text("Effective Date").Format(PRUtil.GetDateFormat()).Flex(3).Align(Alignment.Center) ) 运算符也不会正确重载。此外,默认构造函数为+类留下了未初始化的成员,可能导致未定义的行为(不是100%),这可能会导致一些问题。

所以会发生什么,因为复制构造函数实际上从未将值复制到Integer的{​​{1}}成员,调用默认构造函数,并且std :: cout显示位于n的&#39; a.n&#39;如果没有初始化,可以是任何东西。

来自加州理工学院的

This website对于超载运营商来说是好的,如果不完善的话。

试试这个。

*this

答案 3 :(得分:0)

它具有正确数量的复制ctor调用:

第一个是b,因为operator+(Integer b)按值接受Integer

第二个是由c结果创建的operator+

根据标准,可能会有第3次呼叫:return *this可能已创建临时,稍后会将其复制到c。但实际上它总是被省略。您可以在gcc&amp; clang中关闭此省略,但您无法在MSVC中将其关闭。