2013-05-09 63 views
5

回報考慮一個小單元測試用例取消引用,並通過引用

struct A 
{ 
    virtual void func(){} 
    A& foo() 
    { 
    A *obj = reinterpret_cast<A*>(0xdeadbeef); 
    return *obj; //1 
    } 
}; 

int main() 
{ 
    A obj = obj.foo(); 
} 

在行1是實現定義/未指定,當我們通過引用返回的尊重是不會發生的,並且程序也不會如果崩潰顯式訪問被指向的對象是不是被做出來的?

我與其中一位同事提出過爭論,他提到在大多數情況下,編譯器會優化obj的解引用,因爲我們通過引用來返回它,並且此代碼不會崩潰?

感謝

+0

我的理解是:編譯器沒有義務1)運行導致沒有任何事情發生的代碼2)運行產生未定義行爲的代碼:http://blog.llvm.org/2011/05/what-every- c-programmer-should-know_14.html – Patashu 2013-05-09 06:10:26

+0

即使我相信行爲是未定義的,在這種情況下我們不應該依賴底層實現。 – 2013-05-09 06:11:25

+2

這是未定義的行爲,你不應該依賴未定義的行爲做你認爲它會做的事,是的。但是,如果編譯器確定它是死代碼或未定義的,並且不值得爲代碼生成代碼,從學術的角度來講,它也不一定會崩潰。 – Patashu 2013-05-09 06:12:22

回答

4

我拆開在MS VC8.0的代碼,找東西更有意思:

006D48D6 mov   dword ptr [ebp-8],ecx 
    A *obj = reinterpret_cast<A*>(0xdeadbeef); 
006D48D9 mov   dword ptr [obj],0DEADBEEFh 
     return *obj; //1 
006D48E0 mov   eax,dword ptr [obj] //2 
    } 
006D48E3 mov   esp,ebp 
006D48E5 pop   ebp 
006D48E6 ret  

// 2顯示,只是把obj的地址EAX寄存器作爲返回值。

006D39FC lea   ecx,[ebp-190h] 
006D3A02 call  A::foo (6A8C12h) 
006D3A07 push  eax //3 
006D3A08 lea   ecx,[ebp-190h] 
006D3A0E call  A::A (6B89BEh) 

eax是0xdeadbeef,並推送到臨時局部變量的堆棧。比我們稱複製構造函數(這是微不足道的)。所有這些操作只是傳遞地址(這是非法的,但程序不關心)。因爲A結構沒有綁定到特定對象的任何成員,並且程序不會嘗試通過取消引用來查找特定對象,所以這個炸彈還沒有被解僱。

 A *obj = reinterpret_cast<A*>(0xdeadbeef); 
     A tmp ; 
     temp = *obj; 

即使這是可以的,因爲運營商=也通過引用這實際上傳遞的地址。假如傳給你添加一個成員變量,它會失敗,因爲它會試圖找到並複製成員。

+2

+1,即使它不回答問題。 – qdii 2013-05-09 08:08:54