2013-07-23 99 views
1

我已經查看了auto_ptr <>和auto_ptr_ref <>的內部實現的不同來源。我有這個問題,我不明白爲什麼。auto_ptr_ref執行問題

....從函數中返回'auto_ptr'時,編譯器發現沒有合適的ctor來構造返回的對象。但是有轉換到'auto_ptr_ref'和ctor採取'auto_ptr_ref'構造'auto_ptr'。因此,編譯器創建一個'auto_ptr_ref',它基本上只保存對原始'auto_ptr'的引用,然後從這個對象構造'auto_ptr'。這一切(當然,返回一個對象時,編譯器都會經歷這個過程通常兩次,因爲返回的值是地方複製,但是這並不能改變過程).... (參考http://www.josuttis.com/libbook/auto_ptr.html

在這例如,我模仿的auto_ptr <的實施>和auto_ptr_ref <>和能夠產生結果顯示的確是編譯器穿過過程兩次

的例子具有以下基本特徵:

1)A的拷貝構造函數不需要const引用。 2)A有轉換運算符B 3)A有構造函數A(B);

class B { 
}; 

class A { 
public: 

A() { 
    printf("A default constructor() @ %p\r\n", this); 
} 

A(B) { 
    printf("constructor(B) @ %p\r\n", this); 
} 
A (A &a) { 
    printf("copy constructor(non-const) @ %p\r\n", this); 
} 

operator B() { 
    printf("A convertion constructor(B) @ %p\r\n", this); 
    return B(); 
} 
}; 


A foo() 
{ 
    return A(); 
} 

int main() 
{ 
    A a(foo()); 
} 

所以當A中的(FOO()))執行

1)FOO()生成臨時對象X 2)X轉化爲B型 3)構造A(B )用於構造對象一個

這是輸出:

A default constructor() @ 0xbfea340f 
A convertion constructor(B) @ 0xbfea340f 
constructor(B) @ 0xbfea344f 
A convertion constructor(B) @ 0xbfea344f 
constructor(B) @ 0xbfea344e 

我們可以看到編譯器通過兩次轉換構建步驟2。

這是爲什麼?

+0

'auto_ptr'是一個主要的PITA,即使對於編譯器廠商來說也是如此,而這個'auto_ptr_ref'的複雜性已經幫助了它的命運。現在已棄用。 – MSalters

回答

0

A正在創建3次。一次作爲臨時,並複製兩次。其他編譯器可能會根據其優化設置返回不同的結果。

return A()創建一個臨時A,然後將其複製到返回A這第一個副本是第一次,你看到步驟2和3

然後A a(foo());副本從foo()返回到主變量。再次觸發步驟2和3。

+0

好的。我添加了標誌「-fno-elide-constructors」,結果是一樣的。所以當涉及轉換時,編譯器無法執行RVO。 – ROTOGG

1

如果要調用一個函數返回一個類類型,基本上會發生以下情況:

  • 在電話會議上,該調用函數保留堆棧上的臨時返回值一些空間(它必須由調用函數完成,因爲調用函數在堆棧上分配的任何內容只要函數返回就會被釋放)。它將該空間的地址傳遞給被調用的函數。

  • 在執行return語句時,被調用的函數會將從給定return語句的參數的返回值構造到由調用函數提供的空間中。在你的情況下,這個說法是臨時的A值。

  • 函數返回後,調用者使用被調用函數構造的臨時值來完成代碼所需的任何操作。就你而言,你可以用它來構造一個局部變量。

所以,如果你現有的兩次構建A類型的新對象:一是構建臨時返回值(其續航時間的函數後仍然存在foo回報,直到宣佈結束)從返回語句中顯式生成的臨時表(整個生命週期結束於完整表達式的末尾,即在此情況下相當於從foo返回)。其次,要從foo返回的臨時變量中初始化局部變量a

當然,由於沒有合適的拷貝構造函數可用,所以在這兩個的情況下你都要經過B轉換。這是有效的,因爲return直接初始化,而a你明確地編碼了這種直接初始化。