1. 程式人生 > >C++中ststatic_cast、dynamic_cast、reinterpret_cast、const_cast強制型別轉換

C++中ststatic_cast、dynamic_cast、reinterpret_cast、const_cast強制型別轉換

c語言中我們經常使用類似於 int a =(int)3.14等這種強制型別轉換

標準c++的型別轉換符:static_cast 、dynamic_cast、 reindivter_cast、 const_cast, 以下分別介紹他們的用法以及舉例說明

以下程式碼編譯執行環境:codeblock with gcc in win7(x64)

【1】static_cast 
用法:static_cast < type-id > ( exdivssion ) 

該運算子把exdivssion轉換為type-id型別,但沒有執行時型別檢查來保證轉換的安全性。它主要有如下幾種用法:

①用於類層次結構中基類和子類之間指標或引用的轉換。

  進行上行轉換(把子類的指標或引用轉換成基類表示)是安全的;

  進行下行轉換(把基類指標或引用轉換成子類表示)時,由於沒有動態型別檢查,所以是不安全的。

②用於基本資料型別之間的轉換,如把int轉換成char,把int轉換成enum。這種轉換的安全性也要開發人員來保證。

③把空指標轉換成目標型別的空指標。

④把任何型別的表示式轉換成void型別。

注意:static_cast 不能轉換掉exdivssion的const、volitale、或者__unaligned屬性。

【2】dynamic_cast

用法:dynamic_cast < type-id > ( exdivssion )

該運算子把exdivssion轉換成type-id型別的物件。Type-id必須是類的指標、類的引用或者void *;

如果type-id是類指標型別,那麼exdivssion也必須是一個指標,如果type-id是一個引用,那麼exdivssion也必須是一個引用。

dynamic_cast主要用於類層次間的上行轉換和下行轉換,還可以用於類之間的交叉轉換。

在類層次間進行上行轉換時,dynamic_cast和static_cast 的效果是一樣的;

在進行下行轉換時,dynamic_cast具有型別檢查的功能,比static_cast 更安全。

舉例:下行轉換(把基類的指標或引用轉換成子類表示)

需要注意的是如果基類中不含虛擬函式,dynamic_cast 下行轉換編譯會出錯

#include<iostream>
using namespace std;

class father
{
public:
    void fun1()
    {
        cout<<"this is father fun1 call\n";
    }
    virtual void fun()
    {
        cout<<"this is father fun call\n";
    }
};

class son: public father
{
public:
    void fun2()
    {
        cout<<"this is son fun2 call\n";
    }
    void fun()
    {
        cout<<"this is the son fun call\n";
    }
    int k;
};

int main()
{
    father *pf, f;
    son *ps, s;

    pf = &f;// 基類的指標指向基類物件
    ps = static_cast<son *>(pf);//這種轉換是不安全的,行為是不確定的
    if(ps != NULL)
    {
        ps->fun(); //在本文編譯環境下,執行父類的fun
        //本文編譯環境下,一下語句可以執行
        ps->fun2();
        ps->k = 1;
    }
    ps = dynamic_cast<son *>(pf);//轉換後ps = NULL
    if(ps == NULL)
        cout<<"dynamic_cast: ps = NULL\n";
    cout<<"-----------------------------------------------------------------\n";
    pf = &s; //基類指標開始指向子類物件
    //此時,兩種轉換都是安全的
    ps = static_cast<son *>(pf);
    if(ps != NULL)
    {
        ps->fun();
        ps->fun2();
        ps->k = 1;
    }
    ps = dynamic_cast<son *>(pf);//轉換後ps = NULL
    if(ps != NULL)
    {
        ps->fun();
        ps->fun2();
        ps->k = 2;
    }
}

結果:

this is father fun call
this is son fun2 call
dynamic_cast: ps = NULL
-----------------------------------------------------------------
this is the son fun call
this is son fun2 call
this is the son fun call
this is son fun2 call

舉例:上行轉換(把子類的指標或引用轉換成基類表示)

//類定義同上
int main()
{
    father *pf, f;
    son *ps, s;

    ps = &s;// 子類的指標指向子類物件
    //此時兩種轉換都是安全的
    pf = static_cast<father *>(ps);
    if(pf != NULL)
    {
        pf->fun();
    }
    pf = dynamic_cast<father *>(ps);
    if(pf != NULL)
    {
        pf->fun();
    }

}

 結果:

this is the son fun call
this is the son fun call

舉例: static_cast 用於基本型別之間、基本型別指標和空指標間的轉換(不能用於基本型別指標之間轉換)。

注意:基本型別由於表示數值範圍的不同,因此需要使用者保證轉換的安全。另外dynamic_cast不能用於此類轉換

#include<iostream>
using namespace std;
int main()
{
	//基本型別間的轉換,需要使用者保證安全
	int a = 1000;
	char c = static_cast<char>(a);//不安全,1000超過了char的表示範圍
	cout << c << endl;//簡單地擷取a的低八位,小端為0,即空字元輸出空
	a = 49;
	c = static_cast<char>(a);//安全,輸出字元‘1’
	cout << c << endl;
	//c = dynamic_cast<char>(a); 錯誤
	cout << "-----------------------------------------------------------------\n";
	//void *和基本型別指標的轉換,需要使用者保證轉換安全
	a = 49;
	void *pv;
	pv = &a;
	int *pi = static_cast<int *>(pv);//void * 轉換為int *
	cout << *pi << endl; //輸出49
						 //pi = dynamic_cast<int *>(pv); 錯誤
	char *pc = static_cast<char *>(pv);//void *轉char*
	cout << *pc << endl;//輸出字元‘1’
	void *pv2 = static_cast<void *>(pc);// char * 轉void *
	cout << *((char *)pv2) << endl;////輸出字元‘1’
	system("pause");
}

輸出:


1
-----------------------------------------------------------------
49
1

【3】reinterpret_cast

用法:reinterpret_cast<type-id> (exdivssion)

reinterpret_cast運算子是用來處理無關型別之間的轉換;它會產生一個新的值,這個值會有與原始引數(expressoin)有完全相同的位元位。按照reinterpret的字面意思“重新解釋”,即對資料的位元位重新解釋。

IBM的C++指南 裡明確告訴了我們reinterpret_cast可以,或者說應該在什麼地方用來作為轉換運算子:

  • 從指標型別到一個足夠大的整數型別
  • 從整數型別或者列舉型別到指標型別
  • 從一個指向函式的指標到另一個不同型別的指向函式的指標
  • 從一個指向物件的指標到另一個不同型別的指向物件的指標
  • 從一個指向類函式成員的指標到另一個指向不同型別的函式成員的指標
  • 從一個指向類資料成員的指標到另一個指向不同型別的資料成員的指標

總結來說:reinterpret_cast用在任意指標(或引用)型別之間的轉換;以及指標與足夠大的整數型別之間的轉換;從整數型別(包括列舉型別)到指標型別,無視大小。

注意:static_cast 不能轉換掉exdivssion的const、volitale、或者__unaligned屬性。

舉例:reinterpret_cast用法

int main()
{
   int a = 49;
   int *pi = &a;
   char *pc = reinterpret_cast<char*>(pi);//int * 到char *,使用者自己安全
   cout<<*pc<<endl; //49的ASCII碼對應輸出字元"1"
   unsigned long b = reinterpret_cast<unsigned long>(pc);//char * 轉 unsigned long
   cout<<b<<endl;//輸出pc指向地址(即a的地址)對應的整數
   int *pi2 = reinterpret_cast<int *>(b);//unsigned long 轉 int*
   cout<<*pi2<<endl; //輸出49
}

【4】const_cast 

用法:const_cast<type-id> (exdivssion)

該運算子用來修改型別的const、volatile、__unaligned屬性。除了const 、volatile、__unaligned修飾之外, type_id和exdivssion的型別是一樣的。

常量指標被轉化成非常量指標,並且仍然指向原來的物件;

常量引用被轉換成非常量引用,並且仍然指向原來的物件;常量物件被轉換成非常量物件。

舉例:const_cast用法

int main()
{
   const int a = 100;
   int *b = const_cast<int *>(&a);//const int * 轉int *
   cout<<*b<<endl; //輸出100
   cout<<&a<<" "<<b<<endl; //兩者值相同,表明b指向a的地址,只是const屬性變了
}

總結:

類指標或引用的上行轉換static_cast 和 dynamic_cast 都可以

類指標或引用的下行轉換用dynamic_cast並且判斷轉換後是否為空

基本資料型別之間的轉換用static_cast, 但是由於數值範圍的不同,需要使用者保證轉換的安全性

不同型別之間的指標或引用的轉換用reinterpret_cast,它的本質是對指向記憶體的位元位的重解釋

消除資料的const、volatile、__unaligned屬性,用const_cast