AFAIK,對於指針/引用static_cast,如果類定義此時對編譯器不可見,那麼static_cast
的行爲將與reinterpret_cast
類似。static_cast safety
爲什麼static_cast
對於指針/引用是不安全的並且對數值是安全的?
AFAIK,對於指針/引用static_cast,如果類定義此時對編譯器不可見,那麼static_cast
的行爲將與reinterpret_cast
類似。static_cast safety
爲什麼static_cast
對於指針/引用是不安全的並且對數值是安全的?
總之,由於多重繼承。
在長:
#include <iostream>
struct A { int a; };
struct B { int b; };
struct C : A, B { int c; };
int main() {
C c;
std::cout << "C is at : " << (void*)(&c) << "\n";
std::cout << "B is at : " << (void*)static_cast<B*>(&c) << "\n";
std::cout << "A is at : " << (void*)static_cast<A*>(&c) << "\n";
}
輸出:
C is at : 0x22ccd0
B is at : 0x22ccd4
A is at : 0x22ccd0
注意的是,爲了正確地轉換爲B *的static_cast了改變指針值。如果編譯器沒有C的類定義,那麼它不會知道B是一個基類,它肯定不知道要應用哪個偏移量。
但在這種情況下沒有定義可見,的static_cast不表現得像reinterpret_cast的,它是被禁止:
struct D;
struct E;
int main() {
E *p1 = 0;
D *p2 = static_cast<D*>(p1); // doesn't compile
D *p3 = reinterpret_cast<D*>(p1); // compiles, but isn't very useful
}
一個普通的C風格的演員,(B*)(&c)
確實你說的話:如果結構的定義C是可見的,表明B是基類,那麼它與static_cast相同。如果類型只是前向聲明的,那麼它與reinterpret_cast相同。這是因爲它被設計爲與C兼容,這意味着它必須做C在C中可能做的事情。
static_cast總是知道如何處理內置類型,這實際上是內置的手段。它可以將int轉換爲float,等等。所以這就是爲什麼它對數字類型總是安全的,但是它不能轉換指針,除非(a)它知道它指向的是什麼,(b)指向類型之間有正確的關係。因此它可以將int
轉換爲float
,但不能將int*
轉換成float*
。
由於AndreyT說,還有一個辦法,你可以使用static_cast
不安全,編譯器可能不會救你,因爲代碼是合法的:
A a;
C *cp = static_cast<C*>(&a); // compiles, undefined behaviour
的事情之一static_cast
能做的就是「向下」指向派生類的指針(在這種情況下,C是A的派生類)。但是,如果說實際上並不是派生類,那麼你就註定了。 A dynamic_cast
將在運行時執行檢查,但對於我的示例類C,不能使用dynamic_cast
,因爲A沒有虛擬功能。
您也可以使用static_cast
與void*
進行不安全的事情。
不,您的「AFAIK」不正確。 static_cast
從不表現爲reinterpret_cast
(除非可能在您轉換爲void *
時,雖然此轉換通常不應由reinterpret_cast
執行)。
首先,當static_cast
用於指針或引用的轉換,static_cast
的說明書中明確要求有一定的關係類型之間存在(並且是已知的static_cast
)。對於班級類型,他們將通過繼承關聯,如static_cast
所感知的。如果兩種類型都沒有完全由static_cast
這一點來定義,則無法滿足該要求。所以,如果在static_cast
這個點上定義是不可見的,代碼根本就不會編譯。
爲了說明上述與例子:static_cast
可以使用的冗餘]來執行對象的指針upcasts。該代碼
Derived *derived = /* whatever */;
Base *base = static_cast<Base *>(derived);
只有編譯時,下面的代碼是編譯
Base *base(derived);
併爲此編譯兩種類型的定義必須是可見的。
另外,static_cast
可以被用來執行對象指針向下轉換。該代碼
Base *base = /* whatever */;
Derived *derived = static_cast<Derived *>(base);
只有編譯時,下面的代碼是編譯
Base *base(derived); // reverse direction
,並再次爲這個編譯兩種類型的定義必須是可見的。
所以,你根本就無法使用static_cast
未定義類型。如果你的編譯器允許的話,這是編譯器中的一個錯誤。
static_cast
可以是不安全的用於一個完全不同的原因的指針/引用。 static_cast
可以執行對象指針/引用類型的分層向下轉換,而不檢查對象的實際動態類型。 static_cast
也可以爲方法指針類型執行分層上傳。使用這些未經檢查的強制轉換的結果可能導致未定義的行爲,如果不謹慎的話。
其次,當static_cast
與算術類型一起使用時,語義是完全不同的,並且 與上面沒有任何關係。它只是執行算術類型轉換。只要它們符合你的意圖,它們總是非常安全(除了範圍問題)。實際上,避免使用static_cast
進行算術轉換並使用舊的C風格轉換可能是一種很好的編程風格,只是爲了在始終安全的算術轉換和潛在不安全的分層指針/參考轉換之間的源代碼中提供明顯區別。
多重繼承是不是唯一的問題,AndreyT的回答解決向下轉換到錯誤類型的問題。 – 2010-03-04 21:44:12
的確,我只完全回答了問題的第一段,部分是第二段。不是static_cast安全性的廣泛問題(標題中)。 – 2010-03-05 11:33:46