static_const和reinterpret_cast
static_cast可以用來,將指針或者引用轉為相關的指針或者引用,譬如派生類轉為基類,基類轉為派生類,但是安全性必須由程序員自己控制。因為int*和float *是不相關的,所以它們之前的轉換不能用static_cast,但可以用傳統的強制進行轉換,譬如(int *)
要注意的是static_cast只能對有繼承關系的類,進行向下轉型或者向上轉型,如果是兩個無關的類,用static_cast轉型,編譯錯誤
dynamic_cast在編譯期間,只檢查被轉換類是否有虛表,如果有虛表,則在編譯過程中不會出錯,這是因為dynamic_cast是在運行期間檢查的,如果在運行期間檢查兩個類之間不能轉換,如果是指針則返回NULL,如果是引用則有異常。
class A{ public:virtual void fun(){cout<<"a"<<endl;}};class B { public:};int main() {A a;B *b = dynamic_cast<B*>(&a);if(b == NULL)cout<<"NULL"<<endl;}上述程序如果用static_cast轉型,則編譯出錯
reinterpret_cast,不管兩個類之間是否相關,就都可以轉型,至于實際運行中是否正確,要看實際的平臺。
對于類而言,static_const 只能用于轉換指針或者引用的,不用用于轉換類
class AA{
};
class BB:public AA{
};
void fun(AA &a){
? ? BB b = static_cast<BB>(a);
}
肯定會編譯出錯,應該是? BB& b = static_cast<BB &>(a);
還可以用于可以隱式轉換的情況的非指針或者引用,譬如:
double d=3.14159265; int i = static_cast<int>(d); 或者對象之間的轉換(不是對象的指針或者引用) 對于代碼: class ad1 { public:ad1(){cerr<<"ad1 construction\n";}~ad1(){cerr<<"ad1 deconstruction\n";} };class ad2 { public:ad2(){cerr<<"ad2 construction\n";}~ad2(){cerr<<"ad2 deconstruction\n";} }; class test2; class test1 {public:test1(int a):tst_var(a){cerr<<"test1 construction with para\n"<<tst_var<<endl;}test1(){cerr<<"test1 construction\n";}test1(test1 &t1){cerr<<"test1 copy construction ,parameter is test1\n";}/*test1(test2 &t2){cerr<<"test1 copy construction ,parameter is test2\n";}*/~test1(){cerr<<"test1 deconstruction\n"<<tst_var<<endl;}void testtry() const;void testtry();virtual void calc(){cerr<<"int test1\n"<<"sum is "<<Snum<<endl;++Snum;}void set(int num){ptd1 = num;}void display(){cerr<<"ptdl = "<<ptd1<<endl;} protected:int ptd1;static int Snum ; private://A &a;int tst_var;};class test2:public test1 { public:test2():a2(),a1(),test1(6){cerr<<"test2 construcion\n";a = 6;}void calc(){cerr<<"int test2\n";}void Display(){cerr<<"snum is "<<Snum<<endl;}void anotherFun(){cerr<<"test2 anothre fun "<<a<<endl;}~test2(){cerr<<"test2 deconstruction \n";}protected:int a;ad1 a1;ad2 a2;};在main.cpp test1 t1; test2 *pt2 = static_cast<test2 *>(&t1); pt2->anotherFun(); pt2->calc() pt2->anotherFun()的確調用了test2的函數,但是由于test2的對象沒有定義所以cerr<<"test2 anothre fun ?"<<a<<endl; a的值為隨機但是pt2->calc()卻調用的是test1的函數可以這樣理解,成員函數其實就是普通的函數,只是多傳了一個this指針。(在調用pt2->anotherFun時,t1中沒有這個函數,于是把本來是t1的this指針轉化為t2的指針,就調用了t2的函數;但是對于calc函數而言,t1有對應的實現,所以還是調用t1的calc)對于上面括號里的文字,要持保留意見,應該解釋的不對。pt2->anotherFun,既然pt2是test2的指針,那么就應該調用test2中的函數pt2->calc,因為calc是虛函數,所以要先查pt2所指對象的虛表,因為這個對象是test1的對象,所以其虛表中就是對應test1中函數。如果在test1中calc不是虛函數的話,則pt2->calc將調用test2中的calc如果在main.cpp中是:test2 t2; test1 *rt1 = static_cast<test1 *>(&t2); rt1->calc(); rt1->anotherFun(); 對于rt1->calc()調用的是test2的函數,而rt1->anotherFun()卻編譯出錯,這是因為在test1中沒有anotherFun這個函數。更為詳細的轉換可以參考reinterpret_cast不檢查任何類型,只是簡單的將一個指針的值給另一個指針。 http://www.vckbase.com/document/viewdoc/?id=1651
本文討論static_cast<> 和 reinterpret_cast<>。
介紹
大多程序員在學C++前都學過C,并且習慣于C風格(類型)轉換。當寫C++(程序)時,有時候我們在使用static_cast<>和reinterpret_cast<>時可能會有點模糊。在本文中,我將說明static_cast<>實際上做了什么,并且指出一些將會導致錯誤的情況。
泛型(Generic Types)
01.float?f = 12.3;02.?03.float* pf = &f;04.// static cast<>05.?06.// 成功編譯, n = 1207.?08.int?n =?static_cast(f);09.?10.// 錯誤,指向的類型是無關的(譯注:即指針變量pf是float類型,現在要被轉換為int類型)11.//int* pn = static_cast(pf);12.?13.//成功編譯14.?15.void* pv =?static_cast(pf);16.?17.//成功編譯, 但是 *pn2是無意義的內存(rubbish)18.?19.int* pn2 =?static_cast(pv);20.// reinterpret_cast<>21.?22.//錯誤,編譯器知道你應該調用static_cast<>23.?24.//int i = reinterpret_cast(f);25.?26.//成功編譯, 但是 *pn 實際上是無意義的內存,和 *pn2一樣27.?28.int* pi =?reinterpret_cast(pf);簡而言之,static_cast<> 將嘗試轉換,舉例來說,如float-到-integer,而reinterpret_cast<>簡單改變編譯器的意圖重新考慮那個對象作為另一類型。
指針類型(Pointer Types)
指針轉換有點復雜,我們將在本文的剩余部分使用下面的類:
01.class?CBaseX02.?03.{04.?05.public:06.?07.int?x;08.?09.CBaseX() { x = 10; }10.?11.void?foo() {?printf("CBaseX::foo() x=%d\n", x); }12.?13.};14.class?CBaseY15.?16.{17.?18.public:19.?20.int?y;21.?22.int* py;23.?24.CBaseY() { y = 20; py = &y; }25.?26.void?bar() {?printf("CBaseY::bar() y=%d, *py=%d\n", y, *py);27.}28.?29.};30.class?CDerived :?public?CBaseX,?public?CBaseY31.?32.{33.?34.public:35.?36.int?z;37.?38.};情況1:兩個無關的類之間的轉換
01.// Convert between CBaseX* and CBaseY*02.?03.// CBaseX* 和 CBaseY*之間的轉換04.?05.CBaseX* pX =?new?CBaseX();06.?07.// Error, types pointed to are unrelated08.?09.// 錯誤, 類型指向是無關的10.?11.// CBaseY* pY1 = static_cast(pX);12.?13.// Compile OK, but pY2 is not CBaseX14.?15.// 成功編譯, 但是 pY2 不是CBaseX16.?17.CBaseY* pY2 =?reinterpret_cast(pX);18.?19.// System crash!!20.?21.// 系統崩潰!!22.?23.// pY2->bar();正如我們在泛型例子中所認識到的,如果你嘗試轉換一個對象到另一個無關的類static_cast<>將失敗,而reinterpret_cast<>就總是成功“欺騙”編譯器:那個對象就是那個無關類。
CBaseY* pY1 = static_cast<CBaseY*>(pX); 是錯誤的,因為static_cast不能轉換無關的類型
情況2:轉換到相關的類
01.1. CDerived* pD =?new?CDerived();02.?03.2.?printf("CDerived* pD = %x\n", (int)pD);04.?05.3.06.?07.4.?// static_cast<> CDerived* -> CBaseY* -> CDerived*08.?09.//成功編譯,隱式static_cast<>轉換10.?11.5. CBaseY* pY1 = pD;12.?13.6.?printf("CBaseY* pY1 = %x\n", (int)pY1);14.?15.// 成功編譯, 現在 pD1 = pD16.?17.7. CDerived* pD1 =?static_cast(pY1);18.?19.8.?printf("CDerived* pD1 = %x\n", (int)pD1);20.?21.9.22.?23.10.?// reinterpret_cast24.?25.// 成功編譯, 但是 pY2 不是 CBaseY*26.?27.11. CBaseY* pY2 =?reinterpret_cast(pD);28.?29.12.?printf("CBaseY* pY2 = %x\n", (int)pY2);30.?31.13.32.?33.14.?// 無關的 static_cast<>34.?35.15. CBaseY* pY3 =?new?CBaseY();36.?37.16.?printf("CBaseY* pY3 = %x\n", (int)pY3);38.?39.// 成功編譯,盡管 pY3 只是一個 "新 CBaseY()"40.?41.17. CDerived* pD3 =?static_cast(pY3);42.?43.18.?printf("CDerived* pD3 = %x\n", (int)pD3);01.---------------------- 輸出 ---------------------------02.?03.CDerived* pD = 392fb804.?05.CBaseY* pY1 = 392fbc06.?07.CDerived* pD1 = 392fb808.?09.CBaseY* pY2 = 392fb810.?11.CBaseY* pY3 = 390ff012.?13.CDerived* pD3 = 390fec注意:在將CDerived*用隱式 static_cast<>轉換到CBaseY*(第5行)時,結果是(指向)CDerived*(的指針向后) 偏移了4(個字節)(譯注:4為int類型在內存中所占字節數)。為了知道static_cast<> 實際如何,我們不得不要來看一下CDerived的內存布局。
對于CBaseY *dy = reinterpret_cast<CBaseY *>(&pD);cout<<dy<<endl;dy->bar();
雖然指針dy實際指向的是CDerived對象的地址,在調用bar時,就像前文所講,只是把dy這個指針傳給bar函數,
printf("CBaseY::bar() y=%d, *py=%d\n", y, *py) 輸出y沒有問題,是個隨機數,但是py是個野指針,所以輸出*py時就發生崩潰
CDerived的內存布局(Memory Layout)
?
如圖所示,CDerived的內存布局包括兩個對象,CBaseX 和 CBaseY,編譯器也知道這一點。因此,當你將CDerived* 轉換到 CBaseY*時,它給指針添加4個字節,同時當你將CBaseY*轉換到CDerived*時,它給指針減去4。然而,甚至它即便不是一個CDerived你也可以這樣做。
當然,這個問題只在如果你做了多繼承時發生。在你將CDerived轉換 到 CBaseX時static_cast<> 和 reinterpret_cast<>是沒有區別的。
情況3:void*之間的向前和向后轉換
因為任何指針可以被轉換到void*,而void*可以被向后轉換到任何指針(對于static_cast<> 和 reinterpret_cast<>轉換都可以這樣做),如果沒有小心處理的話錯誤可能發生。
01.CDerived* pD =?new?CDerived();02.?03.printf("CDerived* pD = %x\n", (int)pD);04.CBaseY* pY = pD;?// 成功編譯, pY = pD + 405.?06.printf("CBaseY* pY = %x\n", (int)pY);07.void* pV1 = pY;?//成功編譯, pV1 = pY08.?09.printf("void* pV1 = %x\n", (int)pV1);10.// pD2 = pY, 但是我們預期 pD2 = pY - 411.?12.CDerived* pD2 =?static_cast(pV1);13.?14.printf("CDerived* pD2 = %x\n", (int)pD2);15.?16.// 系統崩潰17.?18.// pD2->bar();01.---------------------- 輸出 ---------------------------02.?03.CDerived* pD = 392fb804.?05.CBaseY* pY = 392fbc06.?07.void* pV1 = 392fbc08.?09.CDerived* pD2 = 392fbc一旦我們已經轉換指針為void*,我們就不能輕易將其轉換回原類。在上面的例子中,從一個void* 返回CDerived*的唯一方法是將其轉換為CBaseY*然后再轉換為CDerived*。
但是如果我們不能確定它是CBaseY* 還是 CDerived*,這時我們不得不用dynamic_cast<> 或typeid[2]。
注釋:
1. dynamic_cast<>,從另一方面來說,可以防止一個泛型CBaseY* 被轉換到CDerived*。
2. dynamic_cast<>需要類成為多態,即包括“虛”函數,并因此而不能成為void*。
http://blog.csdn.net/geeeeeeee/article/details/3427920
dynamic_cast: ? 通常在基類和派生類之間轉換時使用,run-time ? cast const_cast: ? 主要針對const和volatile的轉換.? static_cast: ? 一般的轉換,no ? run-time ? check.通常,如果你不知道該用哪個,就用這個。???
reinterpret_cast: ? 用于進行沒有任何關聯之間的轉換,比如一個字符指針轉換為一個整形數。
?
1)static_cast<T*>(a) 編譯器在編譯期處理 將地址a轉換成類型T,T和a必須是指針、引用、算術類型或枚舉類型。 表達式static_cast<T*>(a), a的值轉換為模板中指定的類型T。在運行時轉換過程中,不進行類型檢查來確保轉換的安全性。 static_cast它能在內置的數據類型間(譬如int 和float之間的轉換,但是int *和float*不能轉換)互相轉換,對于類只能在有聯系的指針類型間進行轉換(也能用于有聯系的對象)。可以在繼承體系中把指針轉換來、轉換去,但是不能轉換成繼承體系外的一種類型
class A { ... }; class B { ... }; class D : public B { ... }; void f(B* pb, D* pd) {D* pd2 = static_cast<D*>(pb);??????? // 不安全, pb可能只是B的指針B* pb2 = static_cast<B*>(pd);??????? // 安全的A* pa2 = static_cast<A*>(pb);??????? //錯誤A與B沒有繼承關系... }
2)dynamic_cast<T*>(a) 在運行期,會檢查這個轉換是否可能。 完成類層次結構中的提升。T必須是一個指針、引用或無類型的指針。a必須是決定一個指針或引用的表達式。 dynamic_cast 僅能應用于指針或者引用,不支持內置數據類型 表達式dynamic_cast<T*>(a) 將a值轉換為類型為T的對象指針。如果類型T不是a的某個基類型,該操作將返回一個空指針。 它不僅僅像static_cast那樣,檢查轉換前后的兩個指針是否屬于同一個繼承樹,它還要檢查被指針引用的對象的實際類型,確定轉換是否可行。 如果可以,它返回一個新指針,甚至計算出為處理多繼承的需要的必要的偏移量。如果這兩個指針間不能轉換,轉換就會失敗,此時返回空指針(NULL)。 很明顯,為了讓dynamic_cast能正常工作,必須讓編譯器支持運行期類型信息(RTTI)。
3)const_cast<T*>(a) 編譯器在編譯期處理 去掉類型中的常量,除了const或不穩定的變址數,T和a必須是相同的類型。 表達式const_cast<T*>(a)被用于從一個類中去除以下這些屬性:const, volatile, 和 __unaligned。 class A { ... }; void f() {const A *pa = new A;//const對象A *pb;//非const對象//pb = pa; // 這里將出錯,不能將const對象指針賦值給非const對象pb = const_cast<A*>(pa); // 現在OK了... } 對于本身定義時為const的類型,即使你去掉const性,在你操作這片內容時候也要小心,只能r不能w操作,否則還是會出錯 const char* p = "123";? char* c = const_cast<char*>(p);? c[0] = 1;?? //表面上通過編譯去掉了const性,但是操作其地址時系統依然不允許這么做。 const_cast操作不能在不同的種類間轉換。相反,它僅僅把一個它作用的表達式轉換成常量。它可以使一個本來不是const類型的數據轉換成const類型的,或者把const屬性去掉。 盡量不要使用const_cast,如果發現調用自己的函數,竟然使用了const_cast,那就趕緊打住,重新考慮一下設計吧。
4)reinterpret_cast<T*>(a) 編譯器在編譯期處理 任何指針都可以轉換成其它類型的指針,T必須是一個指針、引用、算術類型、指向函數的指針或指向一個類成員的指針。 表達式reinterpret_cast<T*>(a)能夠用于諸如char* 到 int*,或者One_class* 到 Unrelated_class*等類似這樣的轉換,因此可能是不安全的。 class A { ... }; class B { ... }; void f() {A* pa = new A;void* pv = reinterpret_cast<A*>(pa);// pv 現在指向了一個類型為B的對象,這可能是不安全的... } 使用reinterpret_cast 的場合不多,僅在非常必要的情形下,其他類型的強制轉換不能滿足要求時才使用。
?
?
== =========================================== == static_cast .vs. reinterpret_cast? == ================================================ reinterpret_cast是為了映射到一個完全不同類型的意思,這個關鍵詞在我們需要把類型映射回原有類型時用到它。我們映射到的類型僅僅是為了故弄玄虛和其他目的,這是所有映射中最危險的。(這句話是C++編程思想中的原話)? static_cast 和 reinterpret_cast 操作符修改了操作數類型。它們不是互逆的;? static_cast 在編譯時使用類型信息執行轉換,在轉換執行必要的檢測(諸如指針越界計算, 類型檢查). 其操作數相對是安全的。 另一方面;reinterpret_cast是C++里的強制類型轉換符,操作符修改了操作數類型,但僅僅是重新解釋了給出的對象的比特模型而沒有進行二進制轉換。 例子如下: int n=9;? double d=static_cast < double > (n);? 上面的例子中, 我們將一個變量從 int 轉換到 double。這些類型的二進制表達式是不同的。 要將整數 9 轉換到 雙精度整數 9,static_cast 需要正確地為雙精度整數 d 補足比特位。其結果為 9.0。
而reinterpret_cast 的行為卻不同:? int n=9;? double d=reinterpret_cast<double & > (n); 這次, 結果有所不同. 在進行計算以后, d 包含無用值. 這是因為 reinterpret_cast 僅僅是復制 n 的比特位到 d, 沒有進行必要的分析.? 因此, 你需要謹慎使用 reinterpret_cast.
?
reinterpret_casts的最普通的用途就是在函數指針類型之間進行轉換。 例如,假設你有一個函數指針數組: typedefvoid(*FuncPtr)();//FuncPtr is一個指向函數的指針,該函數沒有參數,返回值類型為void FuncPtrfuncPtrArray[10];//funcPtrArray是一個能容納10個FuncPtrs指針的數組
讓我們假設你希望(因為某些莫名其妙的原因)把一個指向下面函數的指針存入funcPtrArray數組: int doSomething();
你不能不經過類型轉換而直接去做,因為doSomething函數對于funcPtrArray數組來說有一個錯誤的類型。在FuncPtrArray數組里的函數返回值是void類型,而doSomething函數返回值是int類型。
funcPtrArray[0] = &doSomething;//錯誤!類型不匹配 reinterpret_cast可以讓你迫使編譯器以你的方法去看待它們: funcPtrArray[0] = reinterpret_cast<FuncPtr>(&doSomething); 轉換函數指針的代碼是不可移植的(C++不保證所有的函數指針都被用一樣的方法表示),在一些情況下這樣的轉換會產生不正確的結果
'dynamic_cast'只用于對象的指針和引用 從派生類指針用dynamic_cast轉為基類指針CBaseY時,不做任何檢測但是如果是從基類指針轉為派生類指針時,會檢查兩個方面:1. 它會檢查轉換是否會返回一個被請求的有效的完整對象。不是的話返回null2. 基類指針是否有虛函數,沒有的話,編譯不通過總結
以上是生活随笔為你收集整理的static_const和reinterpret_cast的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: const_cast
- 下一篇: 零钱组合