2016-09-26 77 views
3

我一直在嘗試理解C++中的類型轉換。在下面的代碼中,類B是類A的子類,它們都共享多態關係。上述程序的理解C風格類型轉換和動態轉換的困難

#include <iostream> 
using namespace std; 
class A{ 
    int a; 
    public: 
     virtual void sayhello() {cout<<"Hello from A"<<endl;} 
}; 
class B:public A{ 
    int b; 
    public: 
     void sayhello(){cout<<"Hello from B"<<endl;} 
     void another_func(){cout<<"Hello from B::another_func"<<endl;} 
}; 
int main() { 

    A *temp1 = new A; 
    //Statement 1 
    B *b =(B*) temp1; 

    //Statement 2 
    //B* b = dynamic_cast<B*>(temp1); 


    b->another_func(); 

    delete temp1; 
    return 0; 
} 

輸出 -

Hello from B::another_func 

我有困難的理解下面的問題 -
1)聲明1,我們如何可以投父對象到子object.Logically這應該不正確,因爲現在我們已經擴大了此對象的功能(同一對象現在可以訪問子類功能another_func)。

Java中的類似的代碼產生無錯誤

「不兼容的類型:A不能被轉換到B
B B =(A)溫度;」

現在,如果我們註釋語句1個&取消註釋語句2類似的事情發生了聲明2.

那麼,爲什麼C++允許嗎? 2)現在,如果我們從兩個類中移除sayhello()函數,使它們沒有多態關係,那麼dynamic_cast的語句2不再起作用(因爲我們知道dynamic_cast可以壓倒多態類),但是c樣式轉換語句1仍然產生相同的輸出。
所以我們可以說C風格的演員並不是基於類之間的多態關係。
什麼是基於C風格轉換髮生的參數?

+4

你的程序調用未定義的行爲。幾乎可以發生任何事情;標準的誇張是[電腦允許惡魔飛出你的鼻子](http://www.catb.org/jargon/html/N/nasal-demons.html)。 – user2357112

回答

4

假設您切換註釋,以便註釋C風格演員,並取消註釋dynamic_cast。此外,修改代碼如下:

B* b = dynamic_cast<B*>(temp1); 
if(b == nullptr) 
    cout << "not really a B" << endl; 
else 
    b->another_func(); 

然後,當運行代碼時,它會打印出「不是真正的B」。

如果你看一下what dynamic_cast does

如果轉換成功,dynamic_cast的返回類型NEW_TYPE的值。如果轉換失敗並且new_type是一個指針類型,它將返回該類型的空指針。

因此,使用dynamic_cast你的版本是不確定的行爲,因爲你解引用指針沒有檢查是否投失敗。


現在使用C風格演員的所有不同版本。 The rules是:

當遇到C樣式轉換表達,該編譯器試圖將其解釋爲下面的轉換表達式,以該順序:

一個)的const_cast(表達); b)static_cast(表達式),帶有擴展:對派生類的指針或引用還允許被轉換爲指針或引用明確的基類(反之亦然),即使基類不可訪問(即,此轉換忽略私有繼承說明符)。同樣適用於將指向成員的指針指向非明確的非虛擬基的成員;

c)static_cast(帶擴展名)後跟const_cast;

d)reinterpret_cast(expression);

e)reinterpret_cast後跟const_cast。 即使無法編譯,也會選擇滿足相應投射運算符要求的第一個選項(請參見示例)。如果演員可以以多種方式解釋爲static_cast,然後是const_cast,則無法編譯演員。

根據這些規則,你的C樣式轉換爲相當於

B *b = static_cast<B *>(temp1); 

由於temp1實際上指向一個A對象,取消引用的結果是不確定的行爲,以及(但出於不同的原因) 。


一些一般性的要點:

  1. C++的轉換都是C樣式蒙上了細粒度的版本。如果你必須演員,更喜歡他們。您可以向編譯器傳達您正在投射的內容。

  2. 您可能應該儘量避免在任何情況下投射。如果你使用dynamic_cast,檢查結果。

+0

'static_cast'不會給出未定義的行爲。嘗試解除引用'b'確實。提供未定義行爲的'static_cast'的唯一形式(只在C++ 17中提出)將超範圍數值類型轉換爲枚舉類型。 – Peter

+0

@Peter OMG,這對我來說真是太可怕了。非常感謝!您的評論非常有幫助。 –

+0

@彼得:神聖廢話!你能否提供該提案的鏈接?編輯:[找到它](http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1766) – user2357112

1

C風格的演員陣容未被選中。在你的情況下,它只是告訴編譯器考慮指針temp1的類型B。用你的代碼這是謊言,以任何形式使用這個指針都會導致未定義的行爲。未定義行爲的一個結果是,它發生了你以某種方式期待的事情。 C風格轉換是從C繼承而來的,需要用C++編譯大量的C代碼。

有時C-風格轉換做正確的事情,但也有他們可以執行多種不同的轉換,這是經常尚不清楚他們做哪些操作:鑄造(T)x可以等效爲一個reinterpret_cast<T>(x),一個static_cast<T>(x),一個const_cast<T>(x),或其組合。通常只有static_cast<T>(x)const_cast<T>(x)的組合才能得到明確定義的結果,即使如此也存在未檢查的先決條件。例如,鑄造指針p到基座B使用static_cast<D*>(p)如果p是從指針DD指針直接轉換爲B指針產生只被定義派生D的指針。