最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C++中的四種類型轉(zhuǎn)換

 更新時間:2015年08月25日 09:47:52   投稿:hebedich  
類型轉(zhuǎn)換有c風(fēng)格的,當(dāng)然還有c++風(fēng)格的。c風(fēng)格的轉(zhuǎn)換的格式很簡單(TYPE)EXPRESSION,但是c風(fēng)格的類型轉(zhuǎn)換有不少的缺點(diǎn),有的時候用c風(fēng)格的轉(zhuǎn)換是不合適的,因?yàn)樗梢栽谌我忸愋椭g轉(zhuǎn)換,

1 引子

這篇筆記是根據(jù)StackOverflow上面的一個問題整理而成,主要內(nèi)容是對C/C++當(dāng)中四種類型轉(zhuǎn)換操作進(jìn)行舉例說明。在之前其實(shí)對它們都是有所了解的,而隨著自己在進(jìn)行總結(jié),并敲了一些測試示例代碼進(jìn)行驗(yàn)證之后,對它們的理解又深刻了一些。

總所周知,在C++ 當(dāng)中引入了四種新的類型轉(zhuǎn)換操作符:static_cast, dynamic_cast, reinterpret_cast,還有const_cast。就自己見過的一些C++代碼當(dāng)中,它們的使用其實(shí)并不普遍。不少程序員依然樂于去使用C-like的類型轉(zhuǎn)換,因?yàn)樗鼜?qiáng)大且編寫起來又簡單。據(jù)說C-Like類型轉(zhuǎn)換操作符的作用實(shí)際上已經(jīng)包括了static_cast, const_cast和reinterpret_cast三種操作符,你相信嗎?一起來著看。

注:上面提到的C-Like類型轉(zhuǎn)換操作有如下的兩種形式,這一點(diǎn)大家一定都不會陌生。

(new-type) expression
new-type (expression)

2 static_cast vs dynamic_cast

之所以把static_cast與dynamic_cast兩兄弟放在一起是因?yàn)樗鼈儍烧邔Ρ绕饋砀菀子浀米 J紫?,從名稱上面它們就有語義相對的關(guān)系,一“靜”一“動”。另外,在功能上面也在一定程度上體現(xiàn)了這一對比的特性,如dynamic_cast的Run-time Checkingt,static_cast在編譯時增加的類型檢測。簡單而言:

static_cast: 1)完成基礎(chǔ)數(shù)據(jù)類型,2)同一個繼承體系中類型的轉(zhuǎn)換
dynamic_cast:使用多態(tài)的場景,增加了一層對真實(shí)調(diào)用對象類型的檢查

2.1 從C-Like到static_cast

static_cast對于基礎(chǔ)類型如int, float, char以及基礎(chǔ)類型對應(yīng)指針的處理大多情況下恰如C-Like的轉(zhuǎn)換一樣,不過static_cast會來得更加安全。

char c = 10;      // 1 個字節(jié)
int *p = (int *)&c;  // 4 個字節(jié)(32bit platform)

*p = 5;        // 內(nèi)存踩臟
int *q = static_cast<int *>(&c); // 使用static_cast可在編譯階段將該錯誤檢查出來。


對于自定義類型的處理,相比C-Like而言,它也多了一層保護(hù),也就是它不支持在不屬于同一繼承體系的類型之間進(jìn)行轉(zhuǎn)換。但是C-Like就可以辦到,看下面這個例子:

#include <iostream>

class A
{
public:
 A(){}
 ~A(){}
 
private:
 int i, j;
};

class C
{
public:
 C(){}
 ~C(){}

 void printC()
 {
  std::cout <<"call printC() in class C" <<std::endl;
 }
private:
 char c1, c2;
};

int main()
{ 
 A *ptrA = new A;
 //C *ptrC = static_cast<C *>(ptrA);
 // 編譯無法通過,提示:
 // In function ‘int main()':
 // error: invalid static_cast from type ‘A*' to type ‘C*'
 
 C *ptrC = (C *)(ptrA);
 ptrC->printC();
 // 編譯正常通過。
 // 盡管這個時候能夠正常調(diào)用printC,但實(shí)際上這種做法的結(jié)果是“undefined”
 // 嘗試過,如果添加一些數(shù)據(jù)成員的運(yùn)算,這個時候?qū)沟眠\(yùn)算結(jié)果無法預(yù)測
 // 所以,在運(yùn)行時候該邏輯相關(guān)的行為是不清晰的。
 
 return 0;
} 

2.2 static_cast對于自定義類型的轉(zhuǎn)換

上面這個小例子簡單對比了static_cast與C-Like在針對不同繼承體系的類之間表現(xiàn)的差異性,現(xiàn)在先把范圍縮小到同一繼承體系當(dāng)中的類型轉(zhuǎn)換。(注:這里所說的類型一般是針對類的指針或者類的引用)

static_cast針對同一繼承體系的類之間的轉(zhuǎn)換,它既可以進(jìn)行upcast也可以進(jìn)行downcast。一般來說,在進(jìn)行upcast時是沒有問題的,畢竟子類當(dāng)中一定包含有父類的相關(guān)操作集合,所以通過轉(zhuǎn)換之后的指針或者引用來操作對應(yīng)的對象,其行為上是可以保證沒問題。這和使用static_cast與使用C-Like或者直接隱式轉(zhuǎn)換效果一樣(當(dāng)然,其結(jié)果是否符合程序員本身的預(yù)期與當(dāng)時的設(shè)計有關(guān)系)。

需要注意的是,使用static_cast進(jìn)行downcast應(yīng)該避免,因?yàn)樗梢皂樌舆^編譯器的法眼,但在運(yùn)行時卻會爆發(fā)未定義的問題:

#include <iostream>

class A
{
public:
 A():i(1), j(1){}
 ~A(){}
 
 void printA()
 {
  std::cout <<"call printA() in class A" <<std::endl;
 }
 
 void printSum()
 {
  std::cout <<"sum = " <<i+j <<std::endl;
 }
 
private:
 int i, j;
};

class B : public A
{
public:
 B():a(2), b(2) {}
 ~B(){}

 void printB()
 {
  std::cout <<"call printB() in class B" <<std::endl;
 }
 
 void printSum()
 {
  std::cout <<"sum = " <<a+b <<std::endl;
 }
 
 void Add()
 {
  a++;
  b++;
 }
 
private:
 double a, b;
};

int main()
{   
 B *ptrB = new B;
 ptrB->printSum();
 //打印結(jié)果:sum = 4
 A *ptrA = static_cast<B *>(ptrB);  
 ptrA->printA();
 ptrA->printSum();
 //打印結(jié)果:sum = 2
 //在進(jìn)行upcast的時候,指針指向的對象的行為與指針的類型相關(guān)。
 
 
 ptrA = new A;
 ptrA->printSum();
 //打印結(jié)果:sum = 2 
 ptrB = static_cast<B *>(ptrA);
 ptrB->printB();
 ptrB->printSum(); 
 //打印結(jié)果:sum = 0
 //在進(jìn)行downcast的時候,其行為是“undefined”。
 
 //B b;
 //B &rB = b;
 //rB.printSum();
 //打印結(jié)果:sum = 4
 //A &rA = static_cast<A &>(rB);  
 //rA.printA();
 //rA.printSum();
 //打印結(jié)果:sum = 2
 //在進(jìn)行upcast的時候,引用指向的對象的行為與引用的類型相關(guān)。
 
 //A a;
 //A &rA = a;
 //rA.printSum();
 //打印結(jié)果:sum = 4
 //B &rB = static_cast<B &>(rA);  
 //rB.printB();
 //rB.printSum();
 //打印結(jié)果:sum = 5.18629e-317 
 //在進(jìn)行downcast的時候,其行為是“undefined”。
 
 return 0;
}

如上,static_cast在對同一繼承體系的類之間進(jìn)行downcast時的表現(xiàn),與C-Like針對分屬不同繼承體系的類之間進(jìn)行轉(zhuǎn)換時的表現(xiàn)一樣,將是未定義的。所以,應(yīng)該盡可能使用static_cast執(zhí)行downcast轉(zhuǎn)換,更準(zhǔn)確的說,應(yīng)該盡可能避免對集成體系的類對應(yīng)的指針或者引用進(jìn)行downcast轉(zhuǎn)換。

既然這樣,那是不是在軟件開發(fā)過程當(dāng)中就不會存在downcast的這種情況了呢?實(shí)際上不是的。一般來說,進(jìn)行downcast的時候一般是在虛繼承的場景當(dāng)中,這個時候dynamic_cast就上場了。

2.3 dynamic_cast

dynamic_cast的使用主要在downcast的場景,它的使用需要滿足兩個條件:

downcast時轉(zhuǎn)換的類之間存在著“虛繼承”的關(guān)系
轉(zhuǎn)換之后的類型與其指向的實(shí)際類型要相符合
dynamic_cast對于upcast與static_cast的效果是一樣的,然而因?yàn)閐ynamic_cast依賴于RTTI,所以在性能上面相比static_cast略低。

#include <iostream>
#include <exception>

class A
{
public:
 virtual void print() 
 {
  std::cout <<"Welcome to WorldA!" <<std::endl;
 }
};

class B : public A
{
public:
 B():a(0), b(0) {}
 ~B(){}
 virtual void print() 
 {
  std::cout <<"Welcome to WorldB!" <<std::endl;
 }
private:
 double a, b;
};

int main()
{
 B *ptrB = new B;
 A *ptrA = dynamic_cast<A *>(ptrB);
 ptrA->print();
 //在虛繼承當(dāng)中,針對指針執(zhí)行upcast時dynamic_cast轉(zhuǎn)換的效果與static_cast一樣
 //對是否存在virtual沒有要求,會實(shí)際調(diào)用所指向?qū)ο蟮某蓡T。
  
 //A *ptrA = new A;
 //B *ptrB = dynamic_cast<B *>(ptrA);
 //ptrB->print();
 //Segmentation fault,針對指針執(zhí)行downcast時轉(zhuǎn)換不成功,返回NULL。
 
 //A a;
 //A &ra = a;
 //B &b = dynamic_cast<B &>(ra);
 //b.print();  
 //拋出St8bad_cast異常,針對引用執(zhí)行downcast時轉(zhuǎn)換不成功,拋出異常。
 
 //ptrA = new A;
 //ptrB = static_cast<B *>(ptrA);
 //ptrB->print();
 //使用static_cast進(jìn)行downcast的時候,與dynamic_cast返回NULL不同,
 //這里會調(diào)用ptrB實(shí)際指向的對象的虛函數(shù)。
  
 //ptrA = new A;
 //ptrB = dynamic_cast<B *>(ptrA);
 //ptrB->print();
 //在進(jìn)行downcast時,如果沒有virtual成員,那么在編譯時會提示: 
 // In function ‘int main()':
 // cannot dynamic_cast ‘ptrA' (of type ‘class A*') to type ‘class B*' (source type is not polymorphic)
 
 return 0;
}

從這個例子可以看出,在虛繼承場景下,能夠使用dynamic_cast的地方一定可以使用static_cast,然而dynamic_cast卻有著更嚴(yán)格的要求,以便幫助程序員編寫出更加嚴(yán)謹(jǐn)?shù)拇a。只不過,它在性能上面多了一部分開銷。

3 reinterpret_cast

reinterpret_cast是最危險的一種cast,之所以說它最危險,是因?yàn)樗谋憩F(xiàn)和C-Like一般強(qiáng)大,稍微不注意就會出現(xiàn)錯誤。它一般在一些low-level的轉(zhuǎn)換或者位操作當(dāng)中運(yùn)用。

#include <iostream>

class A
{
public:
 A(){}
 ~A(){}
 void print() 
 {
  std::cout <<"Hello World!" <<std::endl;
 }
};

class B
{
public:
 B():a(0), b(0) {}
 ~B(){}

 void call()
 {
  std::cout <<"Happy for your call!" <<std::endl;
 }

private:
 double a, b;
};

int main()
{
 //A *ptrA = new A;
 //B *ptrB = reinterpret_cast<B *>(ptrA);
 //ptrB->call();
 //正常編譯
 //A *ptrA = new A;
 //B *ptrB = (B *)(ptrA);
 //ptrB->call();
 //正常編譯
 //A *ptrA = new A; 
 //B *ptrB = static_cast<B *>(ptrA);
 //ptrB->call();
 //編譯不通過,提示:
 //In function ‘int main()':
 //error: invalid static_cast from type ‘A*' to type ‘B*'
 
 //char c;
 //char *pC = &c;
 //int *pInt = static_cast<int *>(pC);
 //編譯提示錯誤:error: invalid static_cast from type ‘char*' to type ‘int*'
 //int *pInt = reinterpret_cast<int *>(pC);
 //正常編譯。
 //int *pInt = (int *)(pC);
 //正常編譯。
 
 return 0;
}

分析了static_cast,dynamic_cast與reinterpret_cast之后就可以畫出如下的圖示對它們之間的區(qū)別進(jìn)行簡單比較了。這里沒有將const_cast納入進(jìn)來是因?yàn)樗容^特殊,另外分節(jié)對它進(jìn)行介紹。

     ----------------
     /  dynamic_cast \ -->同一繼承體系(virtual)的類指針或引用[更安全的downcast]
    ~~~~~~~~~~~~~~~~~~~~  
    /   static_cast  \ -->基礎(chǔ)類型[更安全],同一繼承體系的類指針或引用
   ~~~~~~~~~~~~~~~~~~~~~~~~
   /  reinterpret_cast  \ -->與C-Like的作用一致,沒有任何靜態(tài)或者動態(tài)的checking機(jī)制
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  /     C-Like      \ -->基礎(chǔ)類型,同一繼承體系的類指針或引用,不同繼承體系類的指針或引用
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

4 const_cast

const_cast能夠使用來移出或者增加一個變量的const屬性,最初的時候我覺得這個const_cast比較怪異,C里面一直都沒有類似的東西來消除const屬性,這里是否會多余呢?其實(shí),我這種想法本身就沒根沒據(jù)。后來想想,在C++當(dāng)中一直提倡將常量聲明為const,這樣一旦常量變得多了起來,在與其他軟件組件或者第三方庫進(jìn)行銜接的時候就難免會碰到需要cast const屬性的問題。比如:

const int myConst = 15;
int *nonConst = const_cast<int *>(&myConst);

void print(int *p)
{
  std::cout << *p;
}

print(&myConst); // 編譯錯誤:error: invalid conversion from ‘const int*' to ‘int*'
print(nonConst); // 正常

不過,在使用const_cast的時候應(yīng)該要注意,如果沒有必要盡量不要去修改它的值:

const int myConst = 15;
int *nonConst = const_cast<int *>(&myConst);

*nonConst = 10;
// 如果該變量存放在read-only內(nèi)存區(qū)當(dāng)中,在運(yùn)行時可能會出現(xiàn)錯誤。

5 小結(jié)

在C++當(dāng)中對于大部分?jǐn)?shù)據(jù)類型而言,使用C-Like的類型轉(zhuǎn)換已經(jīng)完全夠用了。然而,不少人一直在倡導(dǎo)進(jìn)行顯式數(shù)據(jù)類型轉(zhuǎn)換的時候盡可能地使用C++規(guī)定的類型轉(zhuǎn)換操作。我想這里面大概有兩方面的原因:

第一種,C++是一門“新”的編程語言,應(yīng)該學(xué)會用它本身的思想來解決編程方面的問題;
第二種,盡管C-Like轉(zhuǎn)換操作能力強(qiáng)大,但如果將其任意使用,會產(chǎn)生不少在編譯期間隱藏,卻在運(yùn)行時候神出鬼沒。這些問題使得軟件的行為極不清晰。
如此,C++當(dāng)中引出了其他四種類型轉(zhuǎn)換方式,用來更加安全的完成一些場合的類型轉(zhuǎn)換操作。比如使用reinterpret_cast的時候會表示你確定無疑的想使用C-Like的類型轉(zhuǎn)換;在使用static_cast的時候想要確保轉(zhuǎn)換的對象基本兼容,比如無法將char *轉(zhuǎn)換為int *,無法在不同繼承體系類的指針或引用之間進(jìn)行轉(zhuǎn)換;而使用dynamic_cast的時候是要對虛繼承下的類執(zhí)行downcast轉(zhuǎn)換,并且已經(jīng)明了當(dāng)前性能已經(jīng)不是主要的影響因素......

回答一下前文提到的問題??梢赃@么說,對于const_cast, static_cast, reinterpret_cast和dynamic_cast所能夠完成的所有轉(zhuǎn)換,C-Like也可以完成。但是,C-Like轉(zhuǎn)換卻沒有static_cast, dynamic_cast分別提供的編譯時類型檢測和運(yùn)行時類型檢測。

C++之父Bjarne Stroustrup博士在這里也談到了他的觀點(diǎn),主要有兩點(diǎn):其一,C-Like的cast極具破壞性并且在代碼文本上也難得花不少力氣搜索到它;其二,新式的cast使得程序員更有目的使用它們并且讓編譯器能夠發(fā)現(xiàn)更多的錯誤;其三,新的cast符合模板聲明規(guī)范,可以讓程序員編寫它們自己的cast。

相關(guān)文章

  • 使用OpenGL創(chuàng)建窗口的示例詳解

    使用OpenGL創(chuàng)建窗口的示例詳解

    OpenGL,也就是Open?Graphics?Library。其主要就是用于我們?nèi)ヤ秩?D、3D矢量圖形的一種跨語言、跨平臺的應(yīng)用程序編程接口,這篇文章主要介紹了使用OpenGL創(chuàng)建窗口,需要的朋友可以參考下
    2022-04-04
  • C++ 類中有虛函數(shù)(虛函數(shù)表)時 內(nèi)存分布詳解

    C++ 類中有虛函數(shù)(虛函數(shù)表)時 內(nèi)存分布詳解

    下面小編就為大家?guī)硪黄狢++ 類中有虛函數(shù)(虛函數(shù)表)時 內(nèi)存分布詳解。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-12-12
  • 詳解編譯器編譯原理

    詳解編譯器編譯原理

    這篇文章主要介紹了詳解編譯器編譯原理的相關(guān)資料,需要的朋友可以參考下
    2017-06-06
  • c++ 成員函數(shù)與非成員函數(shù)的抉擇

    c++ 成員函數(shù)與非成員函數(shù)的抉擇

    盡量用類的非成員函數(shù)以及友元函數(shù)替換類的成員函數(shù) 例如一個類來模擬人People
    2013-01-01
  • C語言實(shí)現(xiàn)貪吃蛇游戲(單人版)

    C語言實(shí)現(xiàn)貪吃蛇游戲(單人版)

    這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)貪吃蛇游戲單人版,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-06-06
  • Qt音視頻開發(fā)之視頻文件保存功能的實(shí)現(xiàn)

    Qt音視頻開發(fā)之視頻文件保存功能的實(shí)現(xiàn)

    和音頻存儲類似,視頻的存儲也對應(yīng)三種格式,視頻最原始的數(shù)據(jù)是yuv(音頻對應(yīng)pcm),視頻壓縮后的數(shù)據(jù)是h264(音頻對應(yīng)aac)。本文將利用Qt實(shí)現(xiàn)視頻文件保存功能,感興趣的可以了解一下
    2022-12-12
  • C語言中棧的兩種實(shí)現(xiàn)方法

    C語言中棧的兩種實(shí)現(xiàn)方法

    棧的實(shí)現(xiàn)方式有兩種,一種方法是使用指針,而另一種方法則是使用數(shù)組,這篇文章主要介紹了C語言中棧的兩種實(shí)現(xiàn)方法,需要的朋友可以參考下
    2021-08-08
  • QT使用QChart繪制餅圖

    QT使用QChart繪制餅圖

    在Qt中使用QChart類可以快速繪制一個圖表出來,比如折線圖、餅圖、柱狀圖等,本文就來為大家介紹一下如何利用QChart繪制簡單的餅圖吧
    2024-11-11
  • C++實(shí)現(xiàn)數(shù)據(jù)文件存儲與加載

    C++實(shí)現(xiàn)數(shù)據(jù)文件存儲與加載

    這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)數(shù)據(jù)文件存儲與加載,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-06-06
  • C語言MFC基礎(chǔ)之計算器詳解

    C語言MFC基礎(chǔ)之計算器詳解

    這篇文章主要為大家介紹了MFC實(shí)現(xiàn)簡單的計算器,文中介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2021-08-08

最新評論

兴和县| 吉木乃县| 汉川市| 肃北| 将乐县| 德州市| 伊春市| 化州市| 仪征市| 婺源县| 黑水县| 长子县| 宿迁市| 安宁市| 怀柔区| 苏尼特右旗| 邵东县| 青河县| 宣汉县| 兴隆县| 木里| 凤冈县| 贡嘎县| 华容县| 建始县| 杭州市| 镇赉县| 泽州县| 安平县| 铁岭市| 云龙县| 普定县| 介休市| 蒲江县| 开平市| 新疆| 正阳县| 称多县| 白山市| 常州市| 多伦县|