2012-06-19 142 views
2

在代碼下面有兩個類。創建一個類型爲2的對象,然後將其分配給類1的指針。將一種類型的對象分配給另一種類型

在調用out函數時,調用class 1的out函數。

#include<iostream> 
using namespace std; 

class one 
{ 
    public : 
     void out() 
     { 
      cout<<"one "; 
     } 
}; 

class two 
{ 
    public : 
     void out() 
     { 
      cout<<"two "; 
     } 
}; 

int main() 
{ 
    two dp[3]; 
    one *bp = (one *)dp; 
    for (int i=0; i<3;i++) 
    (bp++)->out(); 
}  

輸出

one one one 

根據我的輸出應該是兩個而不是一個。 當我們創建了類型2的對象時,那個對象的內存位置包含了第二類以外的函數的地址,那麼爲什麼在分配時,第一類被調用?

編輯 - 此外,即使我們改變了第二類功能的名稱,輸出沒有改變。

+1

那麼,你騙了編譯器。你爲什麼期望什麼是合理的? –

+3

這是[未定義的行爲](http://en.wikipedia.org/wiki/Undefined_behavior),簡單明瞭。 – ildjarn

+0

爲什麼undefined? –

回答

4

新手假設全部爲 C++成員函數「屬於」一個對象並不罕見。
正如你注意到的,他們沒有。

概念 - 精確的過程是一個編譯器實現的細節 - 你out成員函數轉化爲「免費」的功能,看起來像這些:

void one_out(one* this) { cout << "one"; } 
void two_out(two* this) { cout << "two"; } 

對於非成員函數,這是所有的需要。

當編譯器看到

(bp++)->out(); 

知道,英國石油公司是一個指向one(它不知道你騙),所以它調用

one_out(bp++); 

,因爲這是編譯器做什麼。

-3

由於您的out()方法未聲明爲virtual,因此將在對象的靜態類型而不是對象的運行時類型上調度它。

此外,兩個類之間沒有子類型關係,因此以這種方式分配指針是不正確的。

+4

'虛擬'也不可靠,因爲這些類之間沒有繼承關係。 –

+0

是的,沒有繼承關係。 –

+0

我編輯了這個答案,請檢查它。 – Wug

1

您的機器上的輸出可能是「一個一個」,但它可能很容易炸燬,&得到冰淇淋,或發射導彈。您的代碼喚起未定義的行爲。

class one 
/*...*/ 

class two 
/*...*/ 

注意onetwo是完全不相關的類。您不是從one得到two,反之亦然。它們是完全不同的類型。

正因爲如此...

two dp[3]; 
one *bp = (one *)dp; 
for (int i=0; i<3;i++) 
    (bp++)->out(); 

此代碼喚起未定義行爲*。bp不指向one類型的對象,它指向two類型的對象。鑑於上述代碼,您不能以這種方式投射指針。

(*注意:未定義的行爲,當你試圖調用一個one方法時,對象竟是two鑄件本身不引起未定義行爲)

您正在使用該轉換語法,(one *)dp是一個C型的演員,在這種情況下歸結爲reinterpret_cast<one*>(bp);的平等。如果沒有其他原因,那麼最好使用reinterpret_cast,這是你真正打算做的事情,而不是編寫自我記錄代碼。

如果你真的想從two*得到one*,你有兩種選擇。

  1. 創建的傳承層次,這樣就可以不用喚起UB
  2. 創建一個轉換操作符,這樣就可以從two反之亦然構建一個one投。

在你的情況,因爲你遍歷one對象的數組,並試圖通過執行這些指針two方法,最好的辦法是上述可能#1。

class one 
{ 
    public : 
     virtual void out() 
     { 
      cout<<"one "; 
     } 
}; 

class two : public one 
{ 
    public : 
     void out() 
     { 
      cout<<"two "; 
     } 
}; 

現在你的循環將工作,代碼將發出「兩個兩個二」,而不是你實際看到的任何隨機行爲。

相關問題