c++強制類型轉換

強制轉換運算符

有幾種特定於 C++ 語言的轉換運算符。這些運算符用於刪除舊式 C 語言轉換中的一些多義性和危險繼承。這些運算符是:

  • dynamic_cast 用於多態類型的轉換。
  • static_cast 用於非多態類型的轉換。
  • const_cast 用於刪除 const、volatile 和 __unaligned 特性。
  • reinterpret_cast 用於位的簡單重新解釋。
  • safe_cast 用於生成可驗證的 MSIL。

在萬不得已時使用 const_cast 和 reinterpret_cast,因爲這些運算符與舊的樣式轉換帶來的危險相同。但是,若要完全替換舊的樣式轉換,仍必須使用它們。

const_cast 運算符

從類中移除 const、volatile 和 __unaligned 特性。

const_cast <type-id> ( expression )

指向任何對象類型的指針或指向數據成員的指針可顯式轉換爲完全相同的類型(const、volatile 和 __unaligned 限定符除外)。對於指針和引用,結果將引用原始對象。對於指向數據成員的指針,結果將引用與指向數據成員的原始(未強制轉換)的指針相同的成員。根據引用對象的類型,通過生成的指針、引用或指向數據成員的指針的寫入操作可能產生未定義的行爲。
不能使用 const_cast 運算符直接重寫常量變量的常量狀態。
const_cast 運算符將 null 指針值轉換爲目標類型的 null 指針值。

// expre_const_cast_Operator.cpp
// compile with: /EHsc
#include <iostream>

using namespace std;
class CCTest {
public:
   void setNumber( int );
   void printNumber() const;
private:
   int number;
};

void CCTest::setNumber( int num ) { number = num; }

void CCTest::printNumber() const {
   cout << "\nBefore: " << number;
   const_cast< CCTest * >( this )->number--;
   cout << "\nAfter: " << number;
}

int main() {
   CCTest X;
   X.setNumber( 8 );
   X.printNumber();
}

在包含 const_cast 的行中,this 指針的數據類型爲 const CCTest 。 const_cast 運算符會將 this 指針的數據類型更改爲 CCTest ,以允許修改成員number。強制轉換僅對其所在的語句中的其餘部分持續。

static_cast 運算符

僅根據表達式中存在的類型,將 expression 轉換爲 type-id, 類型。

static_cast <type-id> ( expression ) 

在標準 C++ 中,不進行運行時類型檢查來幫助確保轉換的安全。在 C++/CX 中,將執行編譯時和運行時檢查。有關詳細信息,請參閱強制轉換 (C++/CX)。
static_cast 運算符可用於將指向基類的指針轉換爲指向派生類的指針等操作。此類轉換並非始終安全。
通常使用 static_cast 轉換數值數據類型,例如將枚舉型轉換爲整型或將整型轉換爲浮點型,而且你能確定參與轉換的數據類型。 static_cast 轉換安全性不如 dynamic_cast 轉換,因爲 static_cast 不執行運行時類型檢查,而 dynamic_cast 執行該檢查。對不明確的指針的 dynamic_cast 將失敗,而 static_cast 的返回結果看似沒有問題,這是危險的。儘管 dynamic_cast 轉換更加安全,但是 dynamic_cast 只適用於指針或引用,而且運行時類型檢查也是一項開銷。有關詳細信息,請參閱 dynamic_cast 運算符。
在下面的示例中,因爲 D 可能有不在 B 內的字段和方法,所以行 D pd2 = static_cast

// 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 特性。有關移除這些特性的詳細信息,請參閱 const_cast Operator。
由於在一個重定位垃圾回收器頂部執行無檢查轉換的危險,你只應在確信代碼將正常運行的時候,在性能關鍵代碼中使用 static_cast。如果必須在發佈模式下使用 static_cast,請在調試版本中用 safe_cast(C++ 組件擴展) 替換它以確保成功。

reinterpret_cast 運算符

允許將任何指針轉換爲任何其他指針類型。也允許將任何整數類型轉換爲任何指針類型以及反向轉換。

reinterpret_cast < type-id > ( expression )

濫用 reinterpret_cast 運算符可能很容易帶來風險。除非所需轉換本身是低級別的,否則應使用其他強制轉換運算符之一。
reinterpret_cast 運算符可用於 char* 到 int* 或 One_class* 到 Unrelated_class* 之類的轉換,這本身並不安全。
reinterpret_cast 的結果不能安全地用於除強制轉換回其原始類型以外的任何用途。在最好的情況下,其他用途也是不可移植的。
reinterpret_cast 運算符不能丟掉 const、volatile 或 __unaligned 特性。有關移除這些特性的詳細信息,請參閱 const_cast Operator。
reinterpret_cast 運算符將 null 指針值轉換爲目標類型的 null 指針值。
reinterpret_cast 的一個實際用途是在哈希函數中,即,通過讓兩個不同的值幾乎不以相同的索引結尾的方式將值映射到索引。

#include <iostream>
using namespace std;

// Returns a hash code based on an address
unsigned short Hash( void *p ) {
   unsigned int val = reinterpret_cast<unsigned int>( p );
   return ( unsigned short )( val ^ (val >> 16));
}

using namespace std;
int main() {
   int a[20];
   for ( int i = 0; i < 20; i++ )
      cout << Hash( a + i ) << endl;
}
Output: 
64641
64645
64889
64893
64881
64885
64873
64877
64865
64869
64857
64861
64849
64853
64841
64845
64833
64837
64825
64829

reinterpret_cast 允許將指針視爲整數類型。結果隨後將按位移位並與自身進行“異或”運算以生成唯一的索引(具有唯一性的概率非常高)。該索引隨後被標準 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
}

給定一個 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>
#include <typeinfo>

struct A {
    virtual void test() {
        printf("in A\n");
   }
};

struct B : A {
    virtual void test() {
        printf("in B\n");
    }

    void test2() {
        printf("test2 in B\n");
    }
};

struct C : B {
    virtual void test() {
        printf("in C\n");
    }

    void test2() {
        printf("test2 in C\n");
    }
};

void Globaltest(A& a) {
    try {
        C &c = dynamic_cast<C&>(a);
        printf("in GlobalTest\n");
    }
    catch(std::bad_cast) {
        printf("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)
    {
        pa->test();
        pb->test();
        pb->test2();
        printf("pa->%p, pb->%p\n", pa, pb);
    }
    else
        printf("Fail to dynamic_cast pa to pb\n");

    C * pc = dynamic_cast<C *>(pa2);
    if (pc)
        pc->test2();
    else
        printf("Fail to dynamic_cast pa2 to pc\n");

    C ConStack;
    Globaltest(ConStack);

   // will fail because B knows nothing about C
    B BonStack;
    Globaltest(BonStack);
}
in C
test2 in B
in GlobalTest
Can't cast to C

bad_cast 異常

由於強制轉換爲引用類型失敗,dynamic_cast 運算符引發 bad_cast 異常。

catch (bad_cast)
   statement

bad_cast 的接口爲:

class bad_cast : public exception {
public:
   bad_cast(const char * _Message = "bad cast");
   bad_cast(const bad_cast &);
   virtual ~bad_cast();
};

以下代碼包含失敗的 dynamic_cast 引發 bad_cast 異常的示例。

// expre_bad_cast_Exception.cpp
// compile with: /EHsc /GR
#include <typeinfo.h>
#include <iostream>

class Shape {
public:
   virtual void virtualfunc() const {}
};

class Circle: public Shape {
public:
   virtual void virtualfunc() const {}
};

using namespace std;
int main() {
   Shape shape_instance;
   Shape& ref_shape = shape_instance;
   try {
      Circle& ref_circle = dynamic_cast<Circle&>(ref_shape); 
   }
   catch (bad_cast b) {
      cout << "Caught: " << b.what();
   }
}

由於強制轉換的對象 (Shape) 不是派生自指定的強制轉換類型 (Circle),因此引發異常。若要避免此異常,請將下列聲明添加到 main:

Circle circle_instance;
Circle& ref_circle = circle_instance;

然後在 try 塊中反轉強制轉換的意義,如下所示:

Shape& ref_shape = dynamic_cast<Shape&>(ref_circle);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章