深刻解析C++中的靜態類型轉換與靜態類型轉換運算符。本站提示廣大學習愛好者:(深刻解析C++中的靜態類型轉換與靜態類型轉換運算符)文章只能為提供參考,不一定能成為您想要的結果。以下是深刻解析C++中的靜態類型轉換與靜態類型轉換運算符正文
dynamic_cast 運算符
將操作數 expression 轉換成類型為type-id 的對象。
語法
dynamic_cast < type-id > ( expression )
備注
type-id 必需是一個指針或援用到之前已界說的類類型的援用或“指向 void 的指針”。假如 type-id 是指針,則expression 的類型必需是指針,假如 type-id 是援用,則為左值。
有關靜態和靜態強迫轉換之間差別的描寫,和各在甚麼情形下合適應用,請拜見 static_cast。
在托管代碼中的 dynamic_cast的行動中有兩個嚴重更改。
為指針的dynamic_cast 對指向裝箱的列舉的基本類型的指針將在運轉時掉敗,則前往 0 而不是已轉換的指針。
dynamic_cast 將不再激發一個異常,當 type-id 是指向值類型的外部指針,則轉換在運轉時掉敗。該轉換將前往 0 指導運轉值而不是激發。
假如 type-id 是指向 expression的明白的可拜訪的直接或直接基類的指針,則成果是指向 type-id 類型的獨一子對象的指針。例如:
// dynamic_cast_1.cpp // compile with: /c class B { }; class C : public B { }; class D : public C { }; void f(D* pd) { C* pc = dynamic_cast<C*>(pd); // ok: C is a direct base class // pc points to C subobject of pd B* pb = dynamic_cast<B*>(pd); // ok: B is an indirect base class // pb points to B subobject of pd }
此轉換類型稱為“向上轉換”,由於它將在類條理構造上的指針,從派生的類移到該類派生的類。向上轉換是一種隱式轉換。
假如 type-id 為 void*,則做運轉時停止檢討肯定 expression的現實類型。成果是指向 by expression 的完全的對象的指針。例如:
// dynamic_cast_2.cpp // compile with: /c /GR class A {virtual void f();}; class B {virtual void f();}; void f() { A* pa = new A; B* pb = new B; void* pv = dynamic_cast<void*>(pa); // pv now points to an object of type A pv = dynamic_cast<void*>(pb); // pv now points to an object of type B }
假如 type-id 不是 void*,則做運轉時停止檢討以肯定能否由 expression 指向的對象可以轉換為由 type-id指向的類型。
假如 expression 類型是 type-id類型的基類,則做運轉時檢討來看能否 expression 確切指向 type-id類型的完全對象。假如為 true,則成果是指向 type-id類型的完全對象的指針。例如:
// dynamic_cast_3.cpp // compile with: /c /GR class B {virtual void f();}; class D : public B {virtual void f();}; void f() { B* pb = new D; // unclear but ok B* pb2 = new B; D* pd = dynamic_cast<D*>(pb); // ok: pb actually points to a D D* pd2 = dynamic_cast<D*>(pb2); // pb2 points to a B not a D }
此轉換類型稱為“向下轉換”,由於它將在類條理構造下的指針,從給定的類移到該類派生的類。
關於多重繼續,引入多義性的能夠性。斟酌下圖中顯示的類條理構造。
關於 CLR 類型,假如轉換可以隱式履行,則 dynamic_cast 成果為 no-op,假如轉換掉敗,則 MSIL isinst 指令將履行靜態檢討並前往 nullptr。
以下示例應用 dynamic_cast 以肯定一個類能否為特別類型的實例:
// dynamic_cast_clr.cpp // compile with: /clr using namespace System; void PrintObjectType( Object^o ) { if( dynamic_cast<String^>(o) ) Console::WriteLine("Object is a String"); else if( dynamic_cast<int^>(o) ) Console::WriteLine("Object is an int"); } int main() { Object^o1 = "hello"; Object^o2 = 10; PrintObjectType(o1); PrintObjectType(o2); }
顯示多重繼續的類條理構造
顯示多繼續的類條理構造
指向類型 D 對象的指針可以平安地強迫轉換為 B 或 C。然則,假如 D 強迫轉換為指向 A 對象的指針,會招致 A 的哪一個實例?這將招致不明白的強迫轉換毛病。若要防止此成績,可以履行兩個明白的轉換。例如:
// dynamic_cast_4.cpp // compile with: /c /GR class A {virtual void f();}; class B {virtual void f();}; class D : public B {virtual void f();}; void f() { D* pd = new D; B* pb = dynamic_cast<B*>(pd); // first cast to B A* pa2 = dynamic_cast<A*>(pb); // ok: unambiguous }
當應用虛擬基類時,其他多義性成績會被引入。斟酌下圖中顯示的類條理構造。
顯示虛擬基類的類條理構造
在此條理構造中,A 是虛擬基類。關於虛擬基類的界說。給定一個 E 類實例和一個指向 A 子對象的指針,指向 B 指針的 dynamic_cast 將掉敗於多義性。必需先將強迫轉換回完全 E 對象,然後以明白的方法反向沿條理構造,達到准確的 B 對象。
斟酌下圖中顯示的類條理構造。
給定一個 E 類型的對象和一個指向 D 子對象的指針,從 D 子對象定位到最左邊的 A 子對象,可停止三個轉換。可以從 D 指針到 E 指針履行 dynamic_cast 轉換,然後從 E 到 B 履行轉換(dynamic_cast 或隱式轉換),最初從 B 到 A 履行隱式轉換。例如:
// dynamic_cast_5.cpp // compile with: /c /GR class A {virtual void f();}; class B : public A {virtual void f();}; class C : public A { }; class D {virtual void f();}; class E : public B, public C, public D {virtual void f();}; void f(D* pd) { E* pe = dynamic_cast<E*>(pd); B* pb = pe; // upcast, implicit conversion A* pa = pb; // upcast, implicit conversion }
dynamic_cast 運算符還可使用履行 “互相轉換”。應用統一個類條理構造能夠停止指針轉換,例如: 從B 子對象轉換到D子對象(只需全部對象是類轉換型E。
斟酌互相轉換,現實上從指針轉換到 D 到指針到最左邊的 A 子對象只需兩個步調。可以從 D 到 B 履行互相轉換,然後從 B 到 A 履行隱式轉換。例如:
// dynamic_cast_6.cpp // compile with: /c /GR class A {virtual void f();}; class B : public A {virtual void f();}; class C : public A { }; class D {virtual void f();}; class E : public B, public C, public D {virtual void f();}; void f(D* pd) { B* pb = dynamic_cast<B*>(pd); // cross cast A* pa = pb; // upcast, implicit conversion }
經由過程 dynamic_cast 將 null 指針值轉換到目的類型的 null 指針值。
當您應用 dynamic_cast < type-id > ( expression )時,假如expression沒法平安地轉換成類型 type-id,則運轉時檢討會惹起變換掉敗。例如:
// dynamic_cast_7.cpp // compile with: /c /GR class A {virtual void f();}; class B {virtual void f();}; void f() { A* pa = new A; B* pb = dynamic_cast<B*>(pa); // fails at runtime, not safe; // B not derived from A }
指針類型的非限制轉換的值是 null 指針。援用類型的非限制轉換會激發 bad_cast 異常。 假如 expression 不指向也不援用有用的對象,則__non_rtti_object 異常激發。
有關異常 __non_rtti_object 的說明,請拜見 typeid。
以下示例創立基類(構造 A)指針,為一個對象(構造 C)。這和在該情形是虛函數,啟用運轉時多態性。
該示例也在條理構造中挪用非虛函數。
// dynamic_cast_8.cpp // compile with: /GR /EHsc #include <stdio.h> #include <iostream> struct A { virtual void test() { printf_s("in A\n"); } }; struct B : A { virtual void test() { printf_s("in B\n"); } void test2() { printf_s("test2 in B\n"); } }; struct C : B { virtual void test() { printf_s("in C\n"); } void test2() { printf_s("test2 in C\n"); } }; void Globaltest(A& a) { try { C &c = dynamic_cast<C&>(a); printf_s("in GlobalTest\n"); } catch(std::bad_cast) { printf_s("Can't cast to C\n"); } } int main() { A *pa = new C; A *pa2 = new B; pa->test(); B * pb = dynamic_cast<B *>(pa); if (pb) pb->test2(); C * pc = dynamic_cast<C *>(pa2); if (pc) pc->test2(); C ConStack; Globaltest(ConStack); // will fail because B knows nothing about C B BonStack; Globaltest(BonStack); }
輸入:
in C test2 in B in GlobalTest
static_cast 運算符
僅依據表達式中存在的類型,將 expression 轉換為 type-id, 類型。
語法
static_cast <type-id> ( expression )
備注
在尺度 C++ 中,不停止運轉時類型檢討來贊助確保轉換的平安。在 C++/CX 中,將履行編譯時和運轉時檢討。
static_cast 運算符可用於將指向基類的指針轉換為指向派生類的指針等操作。此類轉換並不是一直平安。
平日應用 static_cast 轉換數值數據類型,例如將列舉型轉換為整型或將整型轉換為浮點型,並且你能肯定介入轉換的數據類型。 static_cast 轉換平安性不如 dynamic_cast 轉換,由於 static_cast 不履行運轉時類型檢討,而 dynamic_cast 履行該檢討。對不明白的指針的 dynamic_cast 將掉敗,而 static_cast 的前往成果看似沒有成績,這是風險的。雖然 dynamic_cast 轉換加倍平安,然則 dynamic_cast 只實用於指針或援用,並且運轉時類型檢討也是一項開支。
鄙人面的示例中,由於 D 能夠有不在 B 內的字段和辦法,所以行 D* pd2 = static_cast<D*>(pb); 不平安。然則,由於 D 一直包括一切 B,所以行 B* pb2 = static_cast<B*>(pd); 是平安的轉換。
// static_cast_Operator.cpp // compile with: /LD class B {}; class D : public B {}; void f(B* pb, D* pd) { D* pd2 = static_cast<D*>(pb); // Not safe, D can have fields // and methods that are not in B. B* pb2 = static_cast<B*>(pd); // Safe conversion, D always // contains all of B. }
與 dynamic_cast 分歧,pb 的 static_cast 轉換不履行運轉時檢討。由 pb 指向的對象能夠不是 D 類型的對象,在這類情形下應用 *pd2 會是災害性的。例如,挪用 D 類(而非 B 類)的成員函數能夠會招致拜訪抵觸。
dynamic_cast 和 static_cast 運算符可以在全部類條理構造中挪動指針。但是,static_cast 完整依附於轉換語句供給的信息,是以能夠不平安。例如:
// static_cast_Operator_2.cpp // compile with: /LD /GR class B { public: virtual void Test(){} }; class D : public B {}; void f(B* pb) { D* pd1 = dynamic_cast<D*>(pb); D* pd2 = static_cast<D*>(pb); }
假如 pb 確切指向 D 類型的對象,則 pd1 和 pd2 將獲得雷同的值。假如 pb == 0,它們也將獲得雷同的值。
假如 pb 指向 B 類型的對象,而非指向完全的 D 類,則 dynamic_cast 足以斷定前往零。然則,static_cast 依附於法式員的斷言,即 pb 指向 D 類型的對象,因此只是前往指向誰人假定的 D 對象的指針。
是以,static_cast 可以反向履行隱式轉換,而在這類情形下成果是不肯定的。這須要法式員來驗證 static_cast 轉換的成果能否平安。
該行動也實用於類之外的類型。例如,static_cast 可用於將 int 轉換為 char。然則,獲得的 char 能夠沒有足夠的位來保留全部 int 值。異樣,這須要法式員來驗證 static_cast 轉換的成果能否平安。
static_cast 運算符還可用於履行任何隱式轉換,包含尺度轉換和用戶界說的轉換。例如:
// static_cast_Operator_3.cpp // compile with: /LD /GR typedef unsigned char BYTE; void f() { char ch; int i = 65; float f = 2.5; double dbl; ch = static_cast<char>(i); // int to char dbl = static_cast<double>(f); // float to double i = static_cast<BYTE>(ch); }
static_cast 運算符可以將整數值顯式轉換為列舉類型。假如整型值不在列舉值的規模內,生成的列舉值是不肯定的。
static_cast 運算符將 null 指針值轉換為目的類型的 null 指針值。
任何表達式都可以經由過程 static_cast 運算符顯式轉換為 void 類型。目的 void 類型可以選擇性地包括 const、volatile 或 __unaligned 特征。
static_cast 運算符沒法轉換失落 const、volatile 或 __unaligned 特征。