C++中的四種轉型操作符

來源:互聯網
上載者:User

標籤:c++   轉型   編譯器   宏   exception   

在詳細介紹C++中的四種轉型操作符之前,我們先來說說舊式轉型的缺點:

①它幾乎允許將任何類型轉換為任何其他類型,這是十分拙劣的。如果每次轉型都能夠更精確地指明意圖,則更好。

②舊式轉型難以辨識。舊式轉型的文法結構是由一對小括弧加上一個對象名稱組成,而小括弧和對象名稱在C++的任何地方都有可能被使用。

為解決C舊式轉型的缺點,C++匯入了4個新的轉型操作符:static_cast、const_cast、dynamic_cast、reinterpret_cast。下面我來一一分析這四種轉型操作符。

1)static_cast

static_cast基本上擁有與C舊式轉型相同的威力與意義,以及相同的限制。例如,不能夠利用static_cast將一個struct轉型為int,或將一個double轉型為pointer;這些都是C舊式轉型動作原本就不可以完成的任務。static_cast甚至不能夠移除運算式的常量性。

int a,b;

...

double c = static_cast<double>(a)/b;

2)const_cast

const_cast用來改變運算式中的常量性(constness)或變易性(volatileness)。使用const_cast,便是對人類(以及編譯器)強調,通過這個轉型操作符,我們唯一想改變的是某物的常量性或變易性。如果將const_cast應用於上述以為的用途,那麼轉型動作會被拒絕。下面看一個例子:

class Widget{...};

class SpecialWidget:public Widget {...};

void update(SpecialWidget* psw);

SpecialWidget sw;//sw是個non-const對象

const SpecialWidget& csw = sw;//csw卻是一個代表sw的reference,並視之為一個const對象

update(&csw);//錯誤!不能講const SpecialWidget*傳給一個需要SpecialWidget*的函數

update(const_cast<SpecialWidget*>(&csw));//可!&csw的常量性被去除了。也因此,csw(亦即sw)在此函數中可被更改。

update((SpecialWidget*)&csw);//情況同上,但使用的是較難辨識的c舊式轉型文法

Widget* pw = new SpecialWidget;

update(pw);//錯誤!pw的類型是Widget*,但update()需要的卻是SpecialWidget*。

update(const_cast<SpecialWidgt*>(pw));//錯誤!const_cast只能用來影響常量性或變易性,無法進行繼承體系的向下轉型動作。

3)dynamic_cast

①dynamic_cast用來執行繼承體系中”安全地向下轉型或跨系轉型動作“。也就是說你可以利用dynamic_cast,將”指向base class objects的pointers或references“轉型為”指向derived(或sibling base)class objects的pointers或references“,並得知轉型是否成功。如果轉型失敗,會以一個null指標(當轉型對象是指標)或一個exception(當轉型對象是reference)表現出來:

Widget* pw;

...

update(dynamic_cast<SpecialWidget*>(pw));//很好,傳給update()一個指標,指向pw所指的SpecialWidget----如果pw真的指向這樣的東西;否則傳過去的將是一個null指標

void updateViaRef(SpecialWidget& rsw);

updateViaRef(dynamic_cast<SpecialWidget&>(*pw));//很好,傳給updateViaRef()的是pw所指的SpecialWidget----如果pw真的指向這樣的東西;否則拋出一個exception

dynamic_cast只能用來助你巡航於繼承體系之下。它無法應用在缺乏虛函數的類型身上,也不能改變類型的常量性。

②dynamic_cast的第二個用途是找出被某對象佔用的記憶體的起始點。例如:

class HeapTracked

{

public:

     bool isOnheap() const;

private:

     typedef const void* RawAddress;

     static list<RawAddress> addresses;

};

bool HeapTracked::isOnheap() const

{

    const void* rawAddress = dynamic_cast<const void*>(this);//取得一個指標,指向*this所佔記憶體的起始處

    list<RawAddress>::iterator it = find(addresses.begin(),addresses.end(),rawAddress);

    return it != addresses.end();

}

凡涉及”多重繼承或虛擬基類“的對象,會擁有多個地址,只要簡單地將指標”動態轉型“為void*(或const void*或volatile void*或const volatile void*),便會獲得一個指標,指向”原指標所指對象“的記憶體起始處。不過,dynamic_cast只適用於那種”所指對象至少有一個虛函數“的指標身上。

4)reinterpret_cast

這個操作符的轉換結果幾乎總是與編譯平台息息相關。所以reinterpret_cast不具移植性。

reinterpret_cast的最常用用途是轉換”函數指標“類型。假設有一個數組,儲存的都是函數指標,有特定的類型:

typedef void (*FuncPtr)();//FuncPtr是個指標,指向某個函數。

FuncPtr funcPtrArray[10];//funcPtrArray是個數組,內有10個FuncPtrs。

假設由於某種原因,你希望將以下函數的一個指標放進funcPtrArray中:

int doSomething();

如果沒有轉型,不可能辦到這一點,因為doSomething的類型與funcPtrArray所能接受的不同。funcPtrArray內各函數指標所指函數的返回值是void,但doSomething的返回值卻是int:

funPtrArray[0] = &doSomething;//錯誤!類型不對

使用reinterpret_cast,可以強迫編譯器瞭解你的意圖。

funcPtrArray[0] = reinterpret_cast<FuncPtr>(&doSomething);

函數指標的轉型動作並不具有移植性(C++不保證所有的函數指標都能以此方式重新呈現),某些情況下這樣的轉型可能會導致不正確的結果,所以應該盡量避免將函數指標轉型。

如果編譯器尚未支援這些新式轉型動作,也可以使用傳統轉型方式取代static_cast、const_cast和reinterpret_cast。甚至可以利用宏來模擬這些新文法。

#define static_cast(TYPE,EXPR) ((TYPE)(EXPR))

#define const_cast(TYPE,EXPR) ((TYPE)(EXPR))

#define reinterpret_cast(TYPE,EXPR) ((TYPE)(EXPR))

至於dynamic_cast,也可回頭使用舊式的C型文法,或者定義一個宏,但是它們不可能告訴你轉型是否成功。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.