簡體   English   中英

傳遞重載運算符的返回值時,為什么不調用復制構造函數

[英]Why copy constructor is not called when returned value from overloaded operator is passed

考慮以下情形

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與之相關。

您說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

順便說一句:您忘記了在副本ctor中初始化n應該是:

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

您正在見證構造函數的復制效果

當將無名稱的臨時對象(未綁定任何引用)移動或復制到相同類型的對象中(忽略頂級cv限定)時,將忽略復制/移動。 構造該臨時文件后,將直接在將其移動或復制到的存儲中直接構建它。 當無名臨時變量是return語句的參數時,復制省略的這種變體稱為RVO,即“返回值優化”。

要查看預期的輸出(不省略構造函數),請在gcc使用-fno-elide-constructors選項。

編輯

正如我對您的問題Integer:: operator+評論中所提到的那樣。 此外,如其他答案中所述,您的副本構造函數不執行成員的初始化,因此會導致未定義的行為。

為了回答您的問題,復制構造函數從不復制任何內容,並且+運算符未正確重載。 此外,默認構造函數將未初始化的成員留給Integer類,這可能導致未定義的行為(不是該行為的100%),這可能會導致某些問題。

因此發生了什么事,因為復制構造函數實際上從未將值復制到*thisn成員中,因此會調用默認構造函數,並且std :: cout顯示'a.n'的n位置處的任何內容; 如果未初始化,則可能是任何東西。

如果不完美,Caltech的這個網站是很好的參考,可供重載操作員使用。

嘗試這個。

#include <iostream>

using namespace std;


class Integer{
    long long n;
public:
    Integer(long long i):n(i){}
    //Integer(){cout<<"cosntructor";} //This creates an unitialized  instance of integer.

    Integer(){// Do this instead.
        n = 0;
        cout << "Constructor" << endl;
    }

    void print(){
        cout<<n<<endl;
    }
    //Integer(const Integer &a){cout<<"copy constructor"<<" "<<a.n<<endl;} // This isn't a copy contructor.

    Integer(const Integer &a){ //This is a copy contructor.
        this->n = a.n;
        cout << "Copy constructor" << " " << a.n << endl;
    }

    Integer& operator+=(const Integer &);
    Integer operator+(const Integer &);
};

Integer& Integer::operator+=(const Integer &b){ //Always overload incrementors/decrementors first, makes life easier.
    this->n = this->n + b.n;
    return *this;
}


Integer Integer:: operator+(const Integer &b){
     return Integer(*this)+=b.n; //Notice the use of overloaded incrementor inside the '+' operator.
}

int main(){
    // your code goes here
    Integer a(5);
    Integer b(6);
    Integer c(a+b);//line 1
    return 0;
}

它具有正確數量的復制ctor調用:

第一個是b因為operator+(Integer b)按值接受Integer

第二個是由operator+結果創建的c

按照標准,可能會有第三個調用: return *this可能創建了一個臨時文件,以后將其復制到c 但實際上,它總是被忽略。 您可以在gcc&clang中關閉此省略號,而不能在MSVC中將其關閉。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM