2010-03-17 79 views
6

這裏是一本書「C++陷阱」的項目56的摘錄:C++拷貝構造結構和指派問題

的情況並不少見看到一個簡單的 初始化書面 任何一個ÿ對象有三種不同的方式,就好像 它們是相同的。

Y a(1066); 
Y b = Y(1066); 
Y c = 1066; 

就事實而言,所有這三個 初始化的可能會導致相同的目標代碼生成 ,但他們不等價的。 a的初始化被稱爲 直接初始化,它的確如人們所期望的那樣做 。 初始化通過 直接調用Y :: Y(int)來完成。

b和c的初始化爲 更復雜。事實上,他們太 複雜。這些都是複製 初始化。在 初始化B的情況下,我們請求 創立Y類型的匿名臨時 ,用值初始化 1066年,我們使用此匿名臨時作爲參數拷貝 構造函數Y級初始化 b。最後,我們稱這個匿名臨時文件爲 的析構函數。

爲了測試這個,我做了一個帶有數據成員的簡單類(最後附有程序),結果令人驚訝。看來對於c的情況,該對象是由拷貝構造函數構造的,而不是如書中所建議的。

有人知道語言標準是否已經改變,或者這只是編譯器的優化功能嗎?我是使用Visual Studio 2008的

代碼示例:

#include <iostream> 

class Widget 
{ 
    std::string name; 
public: 
    // Constructor 
    Widget(std::string n) { name=n; std::cout << "Constructing Widget " << this->name << std::endl; } 
    // Copy constructor 
    Widget (const Widget& rhs) { std::cout << "Copy constructing Widget from " << rhs.name << std::endl; } 
    // Assignment operator 
    Widget& operator=(const Widget& rhs) { std::cout << "Assigning Widget from " << rhs.name << " to " << this->name << std::endl; return *this; } 
}; 

int main(void) 
{ 
    // construct 
    Widget a("a"); 
    // copy construct 
    Widget b(a); 
    // construct and assign 
    Widget c("c"); 
    c = a; 
    // copy construct! 
    Widget d = a; 
    // construct! 
    Widget e = "e"; 
    // construct and assign 
    Widget f = Widget("f"); 

    return 0; 
} 

輸出:

Constructing Widget a 

Copy constructing Widget from a 

Constructing Widget c 
Assigning Widget from a to c 

Copy constructing Widget from a 

Constructing Widget e 

Constructing Widget f 
Copy constructing Widget from f 

最令我通過構建d和e的結果感到驚訝。確切地說,我期待創建一個空對象,然後創建一個對象並將其分配給空對象。實際上,這些對象是由複製構造函數創建的。

+0

「在初始化b的情況下,我們正在請求創建一個類型爲Y的匿名臨時文件,並使用值1066初始化」...並且在c的初始化過程中也是如此,只是很難看到暫時的。 – 2010-03-17 14:18:00

+0

請注意,如果您的構造函數被聲明爲「explicit」,那麼'Y c = 1006'是不可能的,因爲大多數情況下,任何一個參數構造函數都應該這樣做。 – 2010-03-17 14:28:52

+0

Matthieu,是的,我同意你所說的,我在編程時總是這麼做。如果我沒有記錯的話,你的觀點也包含在「更有效的C++」一書中。 – Andy 2010-03-17 14:39:21

回答

16

語法

X a = b; 

其中A和B是X型的一貫意味着拷貝構造。無論變種,如:

X a = X(); 

使用,不存在分配回事,從來不是。建設和分配是這樣的:

X a; 
a = X(); 
+0

感謝您對複製構建的評論。我並沒有真正意識到這一點 - 我一直認爲你必須做X = a(b);確保調用複製構造函數。 – Andy 2010-03-17 14:09:19

6

編譯器被允許優化個案bca相同。此外,複製構造和賦值操作符調用可以由編譯器完全消除,因此無論您看到的是不同的編譯器甚至編譯器設置都是如此。

+0

那麼這意味着這本書現在可能不是100%正確的?有趣。 – Andy 2010-03-17 14:08:24

+4

@安迪,這本書不是100%正確的。但它不是100%完整的。如果你想100%完成,你需要參考標準。具體來說,它(或你引用的部分)沒有解釋如果從上面的臨時文件進行復制時編譯器可以優化拷貝。 – 2010-03-17 14:10:54

+0

謝謝。我想你應該仍然使用本書提出的格式,以防你使用的編譯器沒有實現優化。 – Andy 2010-03-17 14:19:01

1

由於C++ 17,所有這三個當量(除非Y::Y(int)explicit,這將根本不允許c),因爲通常所稱mandatory copy elision

即使Y c = 1066;只創建一個Y對象,因爲隱式轉換到Y的結果是被用來初始化c而不是創建臨時一個prvalue。