8

考慮下面的代碼的返回值初始化的對象時不叫:複製構造與功能

#include <iostream> 

using namespace std; 

class A 
{ 
    public: 
     int a; 
     A(): a(5) 
     { 
      cout << "Constructor\n"; 
     } 
     A(const A &b) 
     { 
      a = b.a; 
      cout << "Copy Constructor\n"; 
     } 
     A fun(A a) 
     { 
      return a; 
     } 
}; 

int main() 
{ 
    A a, c; 
    A b = a.fun(c); 
    return 0; 
} 

上面的代碼的與g++ file.cpp的輸出是:

Constructor 
Constructor 
Copy Constructor 
Copy Constructor 

的輸出以上代碼與g++ -fno-elide-constructors file.cpp是:

Constructor 
Constructor 
Copy Constructor 
Copy Constructor 
Copy Constructor 

我知道返回值優化通貨膨脹。我的問題是哪個調用複製構造函數是被忽略的(返回期間的臨時對象還是返回的對象被複制到b)?

如果消失的拷貝構造函數是用於創建b的拷貝構造函數,那麼b是如何創建的(因爲在這種情況下也沒有構造函數調用)?

如果我將A b = a.fun(c);行替換爲a.fun(c)並使用第一種方法甚至第二種方法進行編譯,那麼複製構造函數也會被調用2次。所以,如果在上一段中解釋過的情況下,臨時對象的拷貝構造函數被省略了,那麼爲什麼在這種情況下它不被忽略?

+0

我怎麼在我瞭解到這一切時檢查了這些東西,所有東西都是'std :: cout <<「複製構造函數:」<<(void *)b <<「to」<<(void *)this << std :: endl;' ,和'std :: cout <<「構造」<<(void *)this << std :: endl'。如果你在C++中添加移動,獎勵點數11。 – IdeaHat 2014-11-14 19:20:39

回答

6
#include <iostream> 

using namespace std; 

class A 
{ 
public: 
    int a; 
    A(): a(5) 
    { 
     cout << "Constructing: " << (void *)this << std::endl; 
    } 
    A(const A &b) 
    { 
     a = b.a; 
     cout << "Copy Constructor: " << (void *)this << " from " << (void *)&b << std::endl; 
    } 
    A fun(A a) 
    { 
     return a; 
    } 
}; 

int main() 
{ 

    A a, c; 
    A b = a.fun(c); 

    std::cout << "a:" << (void *)&a << std::endl << 
       "b:" << (void *)&b << std::endl << 
       "c:" << (void *)&c << std::endl; 
    return 0; 
} 

收率:

Constructing: 0x7fffbb377220 
Constructing: 0x7fffbb377210 
Copy Constructor: 0x7fffbb377230 from 0x7fffbb377210 
Copy Constructor: 0x7fffbb377200 from 0x7fffbb377230 
a:0x7fffbb377220 
b:0x7fffbb377200 
c:0x7fffbb377210 

所以它構造a,構建c,拷貝c到中間(該函數的自變量a),然後將中間件直接複製到b,跳過典型的a複製到返回中間。如果按值傳遞(改變A fun(const A& a)這是更好的證明:

Constructing: 0x7fff8e9642b0 
Constructing: 0x7fff8e9642a0 
Copy Constructor: 0x7fff8e964290 from 0x7fff8e9642a0 
a:0x7fff8e9642b0 
b:0x7fff8e964290 
c:0x7fff8e9642a0 

一個構造,C構造,C是直接複製到b,儘管b沒有傳遞到樂趣

4

被遺漏的副本是將臨時返回值複製到b中。如果沒有省略號,返回值將從a初始化並複製到b。相反,暫時保留返回值的臨時數據是,它構造成b並用a初始化。 [class.copy]/31:

當尚未結合至參考 (12.2)臨時類對象將被複制/移動到一個類對象具有相同 CV-不合格類型,複製/移動操作可以通過 直接構建臨時對象到 省去拷貝的目標可以省略/移動

您可以觀察到這一點,如果你在fun添加額外的輸出:

A fun(A a) 
{ 
    cout << "fun!" << endl; 
    return a; 
} 

然後與省音,你會得到

[...]
樂趣!
拷貝構造函數

而且沒有:

[...]
樂趣!
複製構造
拷貝構造