C++ 四種類型轉換操作符

在c++中進行類型轉換只需要在變量前加上變量類型,並且轉換是雙向的。

例如:

int i = 0;
double d = 1.9;

int i1 = (int) d;
double d1 = (double) i;

這種類型轉換方式只適用於基本數據類型,對複雜的自定義類型不適用。

因此,C++中提供了四種類型轉換符:static_cast、dynamic_cast、const_cast、reinterpret_cast。


 

1、dynamic_cast

dynamic_cast用於類繼承層次間的指針或引用轉換。主要還是用於執行“安全的向下轉型(safe downcasting)”,也即是基類對象的指針或引用轉換爲同一繼承層次的其他指針或引用。至於“向上轉型”(即派生類指針或引用類型轉換爲其基類類型),本身就是安全的,儘管可以使用dynamic_cast進行轉換,但這是沒必要的, 普通的轉換已經可以達到目的,畢竟使用dynamic_cast是需要開銷的。

不同於其它的強制類型轉換,dynamic_cast在運行時會進行類型檢查,如果綁定到引用或指針的對象不是目標類型的對象,則dynamic_cast失敗:

class Base(){virtual void dummy(){} };
class Derived:public Base{};

Base *b1 = new Base;
Base *b2 = new Derived;

Derived *b2d1 = dynamic_cast<Derived*>(b1);   //轉換失敗,返回NULL
Derived *b2d2 = dynamic_cast<Derived*>(b2);    //轉換成功

Derived &b2d3 = dynamic_cast<Derived&>(*b1);   //轉換失敗,拋出異常
Derived &b2d4 = dynamic_cast<Derived&>(*b2);    //轉換成功

對指針進行dynamic_cast,失敗返回null,成功返回正常cast後的對象指針; 

對引用進行dynamic_cast,失敗拋出一個異常,成功返回正常cast後的對象引用。 

dynamic_cast是四個強制類型轉換操作符中最特殊的一個,它支持運行時識別指針或引用

首先,dynamic_cast依賴於RTTI信息,其次,在轉換時,dynamic_cast會檢查轉換的source對象是否真的可以轉換成target類型,

這種檢查不是語法上的,而是真實情況的檢查。

dynamic_cast與繼承層次的指針

對於“向下轉型”有兩種情況。

一種是基類指針所指對象是派生類類型的,這種轉換是安全的;

另一種是基類指針所指對象爲基類類型,在這種情況下dynamic_cast在運行時做檢查;

#include "stdafx.h"
#include<iostream>
using namespace std;

class Base
{
public:
    Base(){};
    virtual void Show(){cout<<"This is Base calss";}
};
class Derived:public Base
{
public:
    Derived(){};
    void Show(){cout<<"This is Derived class";}
};
int main()
{    
    //這是第一種情況
    Base* base = new Derived;
    if(Derived *der= dynamic_cast<Derived*>(base))
    {
        cout<<"第一種情況轉換成功"<<endl;
        der->Show();
        cout<<endl;
    }
    //這是第二種情況
    Base * base1 = new Base;
    if(Derived *der1 = dynamic_cast<Derived*>(base1))
    {
        cout<<"第二種情況轉換成功"<<endl;
        der1->Show();
    }
    else 
    {
        cout<<"第二種情況轉換失敗"<<endl;
    }

    delete(base);
    delete(base1);
    system("pause");
}

運行結果:

      

 

dynamic_cast和引用類型

在前面的例子中,使用了dynamic_cast將基類指針轉換爲派生類指針,也可以使用dynamic_cast將基類引用轉換爲派生類引用。

同樣的,引用的向上轉換總是安全的:

    Derived c;
    Derived & der2= c;
    Base & base2= dynamic_cast<Base&>(der2);//向上轉換,安全
    base2.Show();

所以,在引用上,dynamic_cast依舊是常用於“安全的向下轉型”。與指針一樣,引用的向下轉型也可以分爲兩種情況,與指針不同的是,並不存在空引用,所以引用的dynamic_cast檢測失敗時會拋出一個bad_cast異常:

int main()
{    
    //第一種情況,轉換成功
    Base &base1 = new Derived;
    Derived &der1 = dynamic_cast<Derived&>(base1);
    cout<<"第一種情況:";
    der1.Show();
    cout<<endl;

    //第二種情況
    Base &base = new Base ;
    cout<<"第二種情況:";
    try{
        Derived & der = dynamic_cast<Derived&>(base);
    }
    catch(bad_cast)
    {
        cout<<"轉化失敗,拋出bad_cast異常"<<endl;
    }
    system("pause");
}

 

運行結果:

使用dynamic_cast轉換的Base類至少帶有一個虛函數

    當一個類中擁有至少一個虛函數的時候,編譯器會爲該類構建出一個虛函數表(virtual method table),虛函數表記錄了虛函數的地址。如果該類派生了其他子類,且子類定義並實現了基類的虛函數,那麼虛函數表會將該函數指向新的地址。虛表是C++多態實現的一個重要手段,也是dynamic_cast操作符轉換能夠進行的前提條件。當類沒有虛函數表的時候(也即一個虛函數都沒有定義),dynamic_cast無法使用RTTI,不能通過編譯(個人猜想...有待驗證)。

  當然,虛函數表的建立對效率是有一定影響的,構建虛函數表、由表查詢函數 都需要時間和空間上的消耗。所以,除了必須聲明virtual(對於一個多態基類而言),不要輕易使用virtual函數。對於虛函數的進一步瞭解,可以查看《Effective C++》


 

2、const_cast

const_cast轉換過程中增加或刪除const屬性。

class Test{};
const Test *t1 = new Test;
Test *t2 = const_cast<Test*>(t1);      //轉換成功

除了添加或刪除const特性,用const_cast符來執行其它類型轉換,都會引起

編譯錯誤。



3、static_cast

static_cast可以完全替代c的類型轉換,而且在對對象指針之間的類型轉換時,可以將父類指針轉換成子類指針,也可以將子類指針轉換成父類指針,但是如果兩個類不相關則無法相互轉換。 需注意的是,如果父類指針指向一個父類對象,此時將父類指針轉換成子類指針雖然可以通過static_cast實現,但是這種轉換很不安全;如果父類指針本身就指向子類指針則不存在安全問題。

class Base(){};
class Derived:public Base{};

Base *b1 = new Base;
Base *b2 = new Derived;

Derived *b2d1 = static_cast<Derived*>(b1);   //轉換成功不安全
Derived *b2d2 = static_cast<Derived*>(b2);    //轉換成功安全

int i = 0;
double d = 1.9;

int d2i = static_cast<int>d;
double i2d = static_cast<double>i;

編譯器隱式執行的任何類型轉換都可以由static_cast顯式完成:

當需要將一個較大的算術類型賦值給較小的類型時,使用強制類型轉換非常有效。此時,強制類型轉換告訴程序的讀者和編譯器:我們知道並且不關心潛在的精度損失。對於從一個較大的算術類型到一個較小類型的賦值,編譯器通常會產生警告。當顯示提供強制類型轉換時,警告信息會被關閉。

編譯器隱式執行的任何類型轉換都可以由 static_cast 顯式完成:
double d = 97.0;
// cast specified to indicate that the conversion is intentional
char ch = static_cast<char>(d);

如果編譯器不提供自動轉換,使用 static_cast 來執行類型轉換也是很有用的。例如,下面的程序使用 static_cast 找回存放在 void* 指針中的值:

void* p = &d; // ok: address of any data object can be stored in a void*
// ok: converts void* back to the original pointer type
double *dp = static_cast<double*>(p);

可通過 static_cast 將存放在 void* 中的指針值強制轉換爲原來的指針類型,此時我們應確保保持指針值。也就是說,強制轉換的結果應與原來的地址值相等。


 

4、reinterpret_cast

reinterpret_cast可以將一種類型的指針直接轉換成另一種類型的指針,不論兩個類型之間是否有繼承關係。而且reinterpret_cast可以將一個指針轉換爲一個整數,也可以把一個整數轉換成一個指針。reinterpret_cast還經常用咋不同函數指針之間的轉換。

class A{};
class B{};

A *a = new A;
B *a2b = reinterpret_cast<B*>(a);     //轉換成功

static_cast 運算符完成*相關類型*之間的轉換. 而 reinterpret_cast 處理*互不相關的類型*之間的轉換.

"互不相關的類型"指的是兩種完全不同的類型,如從整型到指針類型,或者從一個指針到另一個毫不相干的指針.

所以 reinterpret_cast 常常被用作不同類型指針間的相互轉換,因爲所有類型的指針的長度都是一致的(32位系統上都是4字節),按比特位拷貝後不會損失數據.

通常爲操作數的位模式提供較低層次的重新解釋,reinterpret_cast 本質上依賴於機器。爲了安全地使用 reinterpret_cast,要求程序員完全理解所涉及的數據類型,以及編譯器實現強制類型轉換的細節。
 
例如,對於下面的強制轉換:

int *ip;
char *pc = reinterpret_cast<char*>(ip);

變量 pc 所指向的真實對象其實是 int 型,而並非字符數組。任何假設 pc 是普通字符指針的應用,都有可能帶來有趣的運行時錯誤。例如,下面語句用 pc 來初始化一個 string 對象,它可能會引起運行時的怪異行爲。

string str(pc);

問題源於類型已經改變時編譯器沒有提供任何警告或錯誤提示。當我們用 int 型地址初始化 pc 時,由於顯式地聲明瞭這樣的轉換是正確的,因此編譯器不提供任何錯誤或警告信息。後面對 pc 的使用都假設它存放的是 char* 型對象的地址,編譯器確實無法知道 pc 實際上是指向 int 型對象的指針。

因此用 pc 初始化 str 是完全正確的——雖然實際上是無意義的或是錯誤的。查找這類問題的原因相當困難,特別是如果 ip 到 pc 的強制轉換和使用 pc 初始化 string 對象這兩個應用發生在不同文件中的時候。


 

強烈建議:應該避免使用強制類型轉換,強制類型轉換關閉或掛起了正常的類型檢查。

 

內容資料來源於:

https://blog.csdn.net/pyy18829518070/article/details/80077655

https://www.cnblogs.com/xiangtingshen/p/10851851.html

https://www.cnblogs.com/yshl-dragon/archive/2013/03/22/2975376.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章