C++四種cast使用詳細介紹

在C++中,我們經常使用到類型的轉換,像把一個int類型轉換成char類型,一個int類型轉換成double類型,這些轉換屬於隱式類型轉換。而今天我們要來講的是顯式類型轉換。C++提供瞭四種顯式類型轉換,分別是:static_cast、dynamic_cast、const_cast、reinterpret_cast

一、static_cast

static_cast的定義為:

static_cast<type_name>(expression)

type_name是轉換的類型,expression是被轉換的對象或者表達式。

static_cast一般用於隱式轉換,當type_name和express至少有一方可以隱式轉換時,則可以用static進行強制類型轉換。可以用於常見的int、float、double等類型轉換;轉換成功返回true,否則返回false(相當於C語言中的強制類型轉換)。

1、基本數據類型轉換

double serven_double_1 = 1.2;
std::cout<<serven_double_1<<std::endl;
 
int serven_int_1 = static_cast<int>(serven_double_1);
std::cout<<serven_int_1<<std::endl;
 
double serven_double_2 = static_cast<double>(serven_int_1);
std::cout<<serven_double_2<<std::endl;

運行結果:基本類型的轉換,可以看到double類型轉換成int類型後丟失瞭精度,這一點跟reinterpret_cast不一樣,reinterpret_cast是底層二進制的強制拷貝和語義轉換,所以不會丟失精度,後面會講到。

2、指針和void指針的轉換

int* serven_int_2 = new int(2);
void * serven_void_1 = static_cast<void*>(serven_int_2);
int *serven_int_3 = static_cast<int*>(serven_void_1);
*serven_int_2 = 6;
 
std::cout<<*serven_int_2<<std::endl;
std::cout<<*serven_int_3<<std::endl;
std::cout<<serven_void_1<<std::endl;
std::cout<<serven_int_2<<std::endl;
std::cout<<serven_int_3<<std::endl;

 運行結果:void指針和其他類型的指針進行轉化的時候,他們都是指向同一個地址。

 3、父類和子類之間的轉換

class SERVEN_PARENT{
public:
    SERVEN_PARENT(){}
    void Function(){
        std::cout<<"PARENT"<<std::endl;
    }
};
 
class SERVEN_CHILD : public SERVEN_PARENT{
public:
    SERVEN_CHILD(){}
    void Function(){
        std::cout<<"CHILD"<<std::endl;
    }
};
 
 
void main(){
 
    SERVEN_PARENT* ser_par = new SERVEN_PARENT();
    ser_par->Function();
    SERVEN_CHILD* ser_chi = static_cast<SERVEN_CHILD*>(ser_par);
    ser_chi->Function();
 
}

運行結果:在main函數第二行中定義瞭一個ser_chi,是一個派生類對象,然後強制將基類對象轉換成子類,這種叫做下行轉換,轉換後打印的結果是子類的Function,使用static_cast來進行向下轉換是不安全的,因為當子類中定義瞭基類沒有的變量,並且在Function函數中使用瞭這個變量,那麼程序將會報錯。

 下面我們來看一下static_cast不安全的例子:

class SERVEN_PARENT{
public:
    SERVEN_PARENT(){}
    void Function(){
        std::cout<<"PARENT"<<std::endl;
    }
};
 
class SERVEN_CHILD : public SERVEN_PARENT{
public:
    SERVEN_CHILD(){}
    void Function(){
        std::cout<<"CHILD"<<std::endl;
        std::cout<<nums<<std::endl;
    }
 
private:
    char nums = 'g';
};
 
 
void main(){
 
    SERVEN_PARENT* ser_par = new SERVEN_PARENT();
    ser_par->Function();
    SERVEN_CHILD* ser_chi = static_cast<SERVEN_CHILD*>(ser_par);
    ser_chi->Function();
 
}

 運行結果:因為派生類對象使用瞭自己獨有的變量,所以打印char字符的時候就出現瞭亂碼。

二、dynamic_cast

dynamic_cast的定義為:

dynamic_cast<type_name>(expression)

type_name是轉換的類型,expression是被轉換的對象或者表達式。

dynamic一般用於基類指向派生類時的強制轉換,轉換成功返回true,失敗返回false。它不像static_cast一樣向下轉換不安全,它是安全的。它的安全性體現在RTTI,那什麼是RTTI呢?
RTTI是運行時類型識別。程序能夠使用基類的指針或引用來檢查著這些指針或引用所指的對象的實際派生類型(判斷指針原型)。RTTI提供瞭兩個非常有用的操作符:typeid和dynamic_cast。(三個最主要的東西,dynamic_cast,typeid,type_info)。typeid:typeid函數(為type_info類的友元函數,為什麼要這樣呢?目的是防止創建type_info對象)的主要作用就是讓用戶知道當前的變量是什麼類型的,它可以返回一個type_info的引用,可以獲取類的名稱和編碼typeid重載瞭type_info中的==和!=可以用於判斷兩個類型是否相等。
dynamic_cast和static_cast在類繼承的區別就是dynamic_cast向下轉換是安全的。

三、const_cast

const_cast的定義為:

const_cast<type_name>(expression)

type_name是轉換的類型,expression是被轉換的對象或者表達式。

const_case有兩個功能,分別是去掉const和加上const,一般用於去掉const,修改被const修飾為常量的值。但是修改的這個值本身不能是const常量,而是被二次引用或者傳參數時被引用為const,才能修改,否則修改失敗。同時type和express兩個類型要一直去掉const,修改成功返回true,否則返回false。

1、加上const

int* serven_int_4 = new int(2);
const int* serven_int_5 = const_cast<const int*>(serven_int_4);     // 轉換為常量指針
*serven_int_4 = 3;
//*serven_int_5 = 4;              // 不能修改
std::cout<<*serven_int_4<<std::endl;
std::cout<<*serven_int_5<<std::endl;
std::cout<<serven_int_4<<std::endl;
std::cout<<serven_int_5<<std::endl;

2、去掉const

(1)const修飾指針,指針指向一個類對象(常量指針)

將一個常量指針轉換成非常量指針。

class SERVEN_PARENT{
public:
    SERVEN_PARENT(){}
    void Function(){
        std::cout<<"PARENT"<<std::endl;
    }
};
 
class SERVEN_CHILD : public SERVEN_PARENT{
public:
    SERVEN_CHILD(){}
    void Function(){
        std::cout<<"CHILD"<<std::endl;
        std::cout<<nums<<std::endl;
    }
 
private:
    char nums = 'g';
};
 
void main(){
    SERVEN_PARENT ser_par;
    const SERVEN_PARENT* pP = &ser_par;
    SERVEN_PARENT* pP_1 = const_case<SERVEN_PARENT*>(pP);    // 強制將pP轉換成非const
    
}

(2)const修飾指針指向對象的數值(指針常量)

將指針常量轉換為非指針常量。

void main(){
    SERVEN_PARENT ser_par;
    SERVEN_PARENT* const pP = &ser_par;
    SERVEN_PARENT* pP_1 = const_case<SERVEN_PARENT*>(pP);    // 強制將pP轉換成非const
    
}

(3)const修飾指針指向對象的數值並且修飾指針(常量指針常量)

常量指針常量可以被轉換為非常量指針常量,也可以轉換成指針常量或者常量指針。

void main(){
    SERVEN_PARENT ser_par;
    const SERVEN_PARENT* const pP = &ser_par;
    SERVEN_PARENT* pP_1 = const_cast<SERVEN_PARENT*>(pP);    // 強制將pP轉換成非const
    const SERVEN_PARENT* pP_2 = const_cast<SERVEN_PARENT*>(pP);    // 強制將pP轉換成常量指針
    SERVEN_PARENT* const pP_2 = const_cast<SERVEN_PARENT*>(pP);    // 強制將pP轉換成指針常量
 
    
}

四、reinterpret_cast

reinterpret_cast的定義為:

reinterpret_cast<type_name>(expression)

type_name是轉換的類型,expression是被轉換的對象或者表達式。

reinterpret_cast是一種比較粗暴的轉換方式,並且是最不安全的,為什麼說它粗暴呢?因為它直接去拷貝最底層的二進制,它的本質是編譯器的指令,它的作用是可以把一個指針轉換成一個整數,也可以把一個整數轉換成一個指針。或者不同類型的指針相互轉換。

double serven_double_2 = 1.20;
char* serven_char_1 = reinterpret_cast<char* >(&serven_double_2);
double* serven_double_3 = reinterpret_cast<double*>(serven_char_1);
std::cout<<*serven_double_3<<std::endl;
 
int* serven_int_6 = reinterpret_cast<int*>(&serven_double_2);
double* serven_double_4 = reinterpret_cast<double*>(serven_int_6);
std::cout<<*serven_double_3<<std::endl;

運行結果:我們可以看到reinterpret_cast將double類型轉換成int,再轉換成double類型後精度不會丟失,而static_cast會丟失。

到此這篇關於C++四種cast使用詳細介紹的文章就介紹到這瞭,更多相關C++ cast使用內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: