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