首頁 > 軟體

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

2022-07-26 18:03:34

在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使用內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com