亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C++ > 正文

C++中的四種類型轉換

2020-01-26 14:57:42
字體:
來源:轉載
供稿:網友

1 引子

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

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

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

(new-type) expressionnew-type (expression)

2 static_cast vs dynamic_cast

之所以把static_cast與dynamic_cast兩兄弟放在一起是因為它們兩者對比起來更容易記得住。首先,從名稱上面它們就有語義相對的關系,一“靜”一“動”。另外,在功能上面也在一定程度上體現了這一對比的特性,如dynamic_cast的Run-time Checkingt,static_cast在編譯時增加的類型檢測。簡單而言:

static_cast: 1)完成基礎數據類型,2)同一個繼承體系中類型的轉換
dynamic_cast:使用多態的場景,增加了一層對真實調用對象類型的檢查

2.1 從C-Like到static_cast

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

char c = 10;      // 1 個字節int *p = (int *)&c;  // 4 個字節(32bit platform)*p = 5;        // 內存踩臟int *q = static_cast<int *>(&c); // 使用static_cast可在編譯階段將該錯誤檢查出來。


對于自定義類型的處理,相比C-Like而言,它也多了一層保護,也就是它不支持在不屬于同一繼承體系的類型之間進行轉換。但是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(); // 編譯正常通過。 // 盡管這個時候能夠正常調用printC,但實際上這種做法的結果是“undefined” // 嘗試過,如果添加一些數據成員的運算,這個時候將會使得運算結果無法預測 // 所以,在運行時候該邏輯相關的行為是不清晰的。  return 0;} 

2.2 static_cast對于自定義類型的轉換

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

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

需要注意的是,使用static_cast進行downcast應該避免,因為它可以順利逃過編譯器的法眼,但在運行時卻會爆發未定義的問題:

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

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

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

2.3 dynamic_cast

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

downcast時轉換的類之間存在著“虛繼承”的關系
轉換之后的類型與其指向的實際類型要相符合
dynamic_cast對于upcast與static_cast的效果是一樣的,然而因為dynamic_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(); //在虛繼承當中,針對指針執行upcast時dynamic_cast轉換的效果與static_cast一樣 //對是否存在virtual沒有要求,會實際調用所指向對象的成員。   //A *ptrA = new A; //B *ptrB = dynamic_cast<B *>(ptrA); //ptrB->print(); //Segmentation fault,針對指針執行downcast時轉換不成功,返回NULL。  //A a; //A &ra = a; //B &b = dynamic_cast<B &>(ra); //b.print();   //拋出St8bad_cast異常,針對引用執行downcast時轉換不成功,拋出異常。  //ptrA = new A; //ptrB = static_cast<B *>(ptrA); //ptrB->print(); //使用static_cast進行downcast的時候,與dynamic_cast返回NULL不同, //這里會調用ptrB實際指向的對象的虛函數。   //ptrA = new A; //ptrB = dynamic_cast<B *>(ptrA); //ptrB->print(); //在進行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卻有著更嚴格的要求,以便幫助程序員編寫出更加嚴謹的代碼。只不過,它在性能上面多了一部分開銷。

3 reinterpret_cast

reinterpret_cast是最危險的一種cast,之所以說它最危險,是因為它的表現和C-Like一般強大,稍微不注意就會出現錯誤。它一般在一些low-level的轉換或者位操作當中運用。

#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之后就可以畫出如下的圖示對它們之間的區別進行簡單比較了。這里沒有將const_cast納入進來是因為它比較特殊,另外分節對它進行介紹。

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

4 const_cast

const_cast能夠使用來移出或者增加一個變量的const屬性,最初的時候我覺得這個const_cast比較怪異,C里面一直都沒有類似的東西來消除const屬性,這里是否會多余呢?其實,我這種想法本身就沒根沒據。后來想想,在C++當中一直提倡將常量聲明為const,這樣一旦常量變得多了起來,在與其他軟件組件或者第三方庫進行銜接的時候就難免會碰到需要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的時候應該要注意,如果沒有必要盡量不要去修改它的值:

const int myConst = 15;int *nonConst = const_cast<int *>(&myConst);*nonConst = 10;// 如果該變量存放在read-only內存區當中,在運行時可能會出現錯誤。

5 小結

在C++當中對于大部分數據類型而言,使用C-Like的類型轉換已經完全夠用了。然而,不少人一直在倡導進行顯式數據類型轉換的時候盡可能地使用C++規定的類型轉換操作。我想這里面大概有兩方面的原因:

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

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

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

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美福利视频在线| 久久网福利资源网站| 在线观看视频99| 亚洲精品v欧美精品v日韩精品| 成人激情免费在线| 热久久免费国产视频| 久热国产精品视频| 成人h视频在线观看播放| 亚洲欧美日韩精品久久奇米色影视| 激情亚洲一区二区三区四区| 日韩欧美亚洲范冰冰与中字| 国内精品久久久久久中文字幕| 国产高清在线不卡| 亚洲色图在线观看| 日韩在线观看网站| 91精品国产色综合久久不卡98口| 国产精品夜间视频香蕉| 久久国产精品免费视频| 久久久久久香蕉网| 中文欧美在线视频| 91在线播放国产| 亚洲的天堂在线中文字幕| 国产亚洲精品久久久久久| 国产亚洲精品久久久优势| 5566日本婷婷色中文字幕97| 国产精品视频在线播放| 国产精品色视频| 国产一区二区三区免费视频| 欧美性生交xxxxxdddd| 久久久久久久香蕉网| 国产精品一区二区三区免费视频| 日本精品一区二区三区在线| 热草久综合在线| 亚洲精品久久久久久下一站| 国产一区二区成人| 国产欧美日韩免费| 欧美一区二区.| 91在线色戒在线| 日韩69视频在线观看| 久久99热精品这里久久精品| 国产精品精品久久久| 久久高清视频免费| 亚洲最大福利视频| 亚洲精品欧美日韩专区| 欧美国产高跟鞋裸体秀xxxhd| 久久精品人人爽| 欧美国产乱视频| 97精品国产aⅴ7777| 日本精品久久中文字幕佐佐木| 在线精品国产欧美| 精品国产一区二区在线| 欧美性xxxxxxxxx| 亚洲欧美中文日韩在线| 黄网站色欧美视频| 夜夜躁日日躁狠狠久久88av| 一个色综合导航| 欧美日韩加勒比精品一区| 亚洲天堂av在线免费| 欧美激情一区二区三区在线视频观看| 亚洲影院在线看| 国产剧情日韩欧美| 国产一区二区三区欧美| 亚洲精品日产aⅴ| 影音先锋欧美在线资源| 久久久爽爽爽美女图片| 97碰在线观看| 成人av在线亚洲| 日韩欧美国产免费播放| 国产精品一久久香蕉国产线看观看| 亚洲国产精品va在线| 国产美女搞久久| 日韩在线免费视频观看| 欧美黑人巨大精品一区二区| 久久久中文字幕| 一区二区三区视频免费在线观看| 亚洲欧美日韩一区二区三区在线| 97超级碰碰人国产在线观看| 亚洲人成在线播放| 国产精品成久久久久三级| 色yeye香蕉凹凸一区二区av| 尤物99国产成人精品视频| 欧美一区二区视频97| 国产精品高潮在线| 亚洲a∨日韩av高清在线观看| 国产精品久久久| 国产精品青青在线观看爽香蕉| 精品久久久久久久久久国产| 欧美黄色片在线观看| 一本色道久久88综合亚洲精品ⅰ| 国产一区二区免费| 国产精品久久久久久久久久三级| 欧美猛少妇色xxxxx| 国产精品青青在线观看爽香蕉| 亚洲欧美在线免费观看| 久久久久日韩精品久久久男男| 亚洲自拍偷拍第一页| 九九久久综合网站| 国产精品网站大全| 亚洲欧美在线免费观看| 日本精品性网站在线观看| 狠狠躁夜夜躁久久躁别揉| 亚洲人成网站免费播放| 久久琪琪电影院| 欧美中文在线免费| 欧美寡妇偷汉性猛交| 奇门遁甲1982国语版免费观看高清| 亚洲91精品在线| 欧美日韩成人在线视频| 国产精品一区二区三区毛片淫片| 国产一区二区日韩精品欧美精品| 色yeye香蕉凹凸一区二区av| 91社区国产高清| 国产精品成av人在线视午夜片| 91九色精品视频| 日韩在线视频播放| 福利一区福利二区微拍刺激| 伊人久久久久久久久久久久久| 久久影院资源站| 国产日韩在线看片| 欧美成人精品一区| 麻豆一区二区在线观看| 亚洲一区二区久久久久久久| 成人h猎奇视频网站| 日韩中文字幕在线播放| 日本精品视频在线| 欧美激情视频免费观看| 亚洲人av在线影院| 亚洲精品久久7777777| 中文字幕亚洲综合久久| 久久精品中文字幕电影| 日韩精品黄色网| 国产精品久久久久久久久久新婚| 国产精品91久久久久久| 国产精品一久久香蕉国产线看观看| 色噜噜狠狠色综合网图区| 久久久伊人日本| 国产精品主播视频| 日韩免费观看高清| 欧美电影在线观看网站| 日本免费在线精品| 91久久夜色精品国产网站| 亚洲国产精品成人av| 精品成人国产在线观看男人呻吟| 在线观看日韩视频| 欧美日韩中文在线| 国产美女被下药99| 亚洲色图欧美制服丝袜另类第一页| 狠狠久久五月精品中文字幕| 久久人人爽人人爽爽久久| 久久夜色精品亚洲噜噜国产mv| 亚洲精品网站在线播放gif| 中文字幕亚洲图片| 亚洲美女又黄又爽在线观看| 成人综合网网址| 成人综合国产精品| 国产日韩欧美中文在线播放| 亚洲高清免费观看高清完整版| 在线观看日韩欧美| 日韩在线播放一区| 久久国产精品久久久久| 欧美激情精品久久久久久免费印度| 欧美一级片一区| 欧美成人手机在线| 久久好看免费视频|