2011-08-25 220 views
8

在書中泛型編程和STL(中國版),它說:構造函數或複製構造函數?

X x = X()將調用拷貝構造函數。

對我來說這似乎有點不可思議。我寫這樣的

#include <iostream> 

class Test { 

public: 

    Test() { 
     std::cout << "This is ctor\n"; 
    } 

    Test(const Test&) { 
     std::cout << "This is copy-ctor\n"; 
    } 

}; 

int main(int argc, char** argv) 
{ 

    Test t = Test(); 
    return 0; 
} 

輸出一個測試程序是「這是構造函數」。好吧,現在我很困惑,這是對的?

回答

9

很小的是,臨時是默認構造的,然後調用複製構造函數將其複製到對象t中。

然而,在實踐中,副本可被優化出— 即使它具有副作用(控制檯輸出):

[n3290: 8.5/16]:[..]在某些情況下,一種實施方式是 允許通過直接將中間結果 構造到被初始化的對象中來消除直接初始化中固有的複製;見12.2,12.8。

和(在具有相同的子句中給出的示例一起):

[n3290: 12.2/2]:[..]的實現可以(2)傳遞之前,將使用臨時在其中 構建X到f()使用X的副本 構造函數;或者,X(2)可能在用於保存參數的空間 中構建。 [..]

但拷貝構造函數仍然存在,即使它可能不會被調用。

無論如何,如果你用的優化編譯關閉(或與海灣合作委員會,可能-fno-elide-constructors),您將看到:

This is ctor 
This is copy-ctor 
+2

在gcc中,你可能不得不使用'-fno-elide-constructors',因爲即使'-O0'也不能阻止elision,我想。 –

+0

@Kerrek:謝謝! –

+0

即使不是微不足道的*副本也可以被省略,通過構造臨時代替局部變量來消除副本。對象或副本的複雜性與該優化無關。 –

4

從理論上講,X x = X()將調用默認的構造函數創建一個臨時對象,使用複製構造函數將其複製到x中。

實際上,編譯器可以直接跳過複製構造部分並默認構造x(正如David在他的評論中指出的那樣,仍然需要複製構造函數在語法上可訪問)。大多數編譯器至少在啓用優化時執行此操作。

+4

重要的是要注意,拷貝構造函數必須是可用的,即使拷貝沒有被刪除也是如此。也就是說,如果它不可訪問,編譯器會拒絕錯誤的行。 –

+0

@David:你說得對。我已經將其納入我的答案。感謝您提出這個問題。 – sbi

2

這種情況下,Return Value Optimisation(RVO)(也稱爲Copy Elision)的形式可以幫助您優化很多。鏈接的維基百科頁面對發生的事情有非常好的解釋。

+3

我不認爲這是嚴格正確的。這裏沒有函數調用返回一個'X'(記住構造函數沒有返回值)。這是複製elision,這是一個相關的,但獨特的概念。 –

+0

我同意。這不是嚴格的RVO(儘管它是相關的)。 –

+0

@Tomak:這也不完全錯誤。這是*那種*優化的變體。 +1來抵消downvote。 –