C++實例講解四種類型轉換的使用
C++類型轉換
C語言風格的轉換
C語言提供了自己的一套轉換規(guī)則,有好處也有壞處。
C語言的風格:(type_name)expression;
C語言提供了隱式類型轉換和顯式類型轉換。顯式類型轉換一般也叫做強轉,隱式類型轉換編譯器完成,如果轉換不了就報錯。
而C語言類型轉換的風格好處就是簡單,缺陷比如轉換的可視性差,顯式類型轉換的寫法就只有一種,難以精準的跟蹤錯誤。
??char ch=1.1,char ch=1在.cpp下都是合法的,這就是隱式類型轉換,C語言下如果把一個結構體給int就會報錯,因為編譯器不知道怎么去轉,C++下可以通過實現(xiàn)operator int()實現(xiàn)類型轉換或者提供合適的構造函數(shù)完成隱式類型轉換,下面以在.cpp中實現(xiàn)operator int()為例
下面這段代碼由于隱式類型轉換導致了死循環(huán)
C語言的類型轉換其實已經(jīng)足夠完成需求了,但是可視性不太好,比如你不能在代碼庫中搜索它(就一對括號怎么去搜索),所以C++提供了一套類型轉換,相當于語法糖了,此外還會進行一些編譯性檢查(比如dynamic_cast轉換失敗則返回空指針)。但其實作用都是一樣的。
C++風格的類型轉換
C++標準為了增加類型轉換的可見性,提供了四種類型轉化的方式。
static_cast
靜態(tài)類型轉換,進行相關類型的轉換,但不能轉換兩個不相關的類型(即編譯器看到這個轉換是行不通的就會報錯)。
static_cast < type-id > ( expression )
int main() { double a = (int)5.5 + 5.3;//結果是10.3 double b = static_cast <int>(5.5) + 5.3; cout << "a: " << a << endl; cout << "b: " << b << endl; return 0; }
轉換不相關的類型:
reinterpret_cast
很暴力的一個操作符,由英文直譯過來就是重新解釋的轉型。白話就是將一段內(nèi)存重新解釋。
網(wǎng)上看到很多大佬的理解,這里借用或摻雜自己的思想:static_cast是做類型能做的轉換,不行編譯器就報錯,告訴你這樣是不合理的,reinterpret_cast則是就算不能轉換編譯器你也別報錯,我心里有數(shù)。由于reinterpret_cast本質上是一個編譯器指令,所以實際動作完全取決于編譯器,失去了移植性。
暴力歸暴力,但也是合理范圍內(nèi)的,比如你把一個結構體硬塞給int肯定是不行的,但是把結構體指針重新解釋為int那是一點問題沒有。
此外reinterpret_cast重新解釋的辦法是把那一塊內(nèi)存的比特位全部復制下來重新解釋。
下面的例子把一個結構體指針解釋為int再加上5.3。
struct Test { int a; }; int main() { Test* t = new Test; double c = reinterpret_cast <int>(&t) + 5.3; cout << c << endl; return 0; }
const_cast
刪除變量的const屬性,一般和指針或引用連用
int main() { const int a = 1;//a是常變量,在棧上 int* p = const_cast<int*>(&a); *p = 2; cout << a << endl; cout << *p << endl; return 0; }
volatile const int a=1;//打印的a就會是2
volatile后編譯器對訪問該變量的代碼不再優(yōu)化。比如上面那句代碼打印1就是因為編譯器優(yōu)化,讀取的a是從寄存器中讀取的,而不是內(nèi)存。
多線程中也有很多關于 volatile的應用。
dynamic_cast
dynamic_cast,安全的向下轉型。
多態(tài)的轉換中向下轉型(父類轉為子類)用dynamic是安全的,但是父類必須有虛函數(shù),否則編譯報錯,且只能用于指針或引用。
向上轉型,子類給父類,發(fā)生切割,不需要轉換。
向下轉型,父類地址給子類指針,需要類型轉換,由于是類型是子類指針,但是給的地址是父類的,如果用指針去訪問子類獨有的數(shù)據(jù)可能就會造成越界,然后程序就可能崩了,所以此時就需要dynamic_cast進行轉換了,因為dynamic_cast會檢查父類指針是否指向的是這個子類,如果是父類說明可能帶來一些安全問題,會轉換失敗返回空指針,反之則實現(xiàn)轉換。
理解向下轉型不安全有個例子:男人是人,但人不一定是男人(男人派生自人,男人的指針=(男人類型的指針強轉)人的地址,此時如果通過男人指針去調(diào)用男人自己獨有的特性就是越界,因為實際上拿到的是人的地址,并沒有存儲男人的特性,也可以從空間大小上來解釋,人是父類,空間更小,男人是子類,空間更大,給的是小空間,卻有了訪問大空間的能力,所以可能造成越界,所以是不安全的)
此外在一個父類有多個子類且需要類型轉換時,dynamic_cast也能起到作用,舉個例子
//父類是Entity,子類是Player和Enemy, e,player,enemy分別表示他們對應的實例化對象 Entity* p=&player; Enemy* p1=(Enemy*)p;//向下轉型需要強轉,這樣搞就出問題了,p明明指向的是一個player,現(xiàn)在類型為Enemy的指針居然指向了一個Player的對象,如果對其內(nèi)存進行了操作,那后果是不可預料的。
父類指針一開始指向一個父類對象,再給到子類類型的指針,存在越界的風險,如果為了保證安全則需要檢查。
父類的指針指向子類對象(向上轉型),再給到子類指針,合理。
所以建議使用dynamic_cast,因為可以保證安全, 轉換失敗返回空指針(NULL)。
dynamic_cast的底層與RTTI有關,借助RTTI拿到類型信息,所以dynamic_cast更像一個函數(shù),因為不是編譯指令,所以會帶來一些性能的損失。
RTTI 是“Runtime Type Information”的縮寫,意思是運行時類型信息。RTTI存儲了所有類型運行時的類型信息,增加了開銷但是可以讓我們做更多的操作。dynamic_cast的底層就是借助了RTTI+匹配,具體的不太了解。VS可以關閉RTTI,但是也就不能用dynamic_cast了
下面給一個代碼例子
class Entity { public: virtual void Print() {};//dynamic_cast使用的前提必須是父類有虛函數(shù) }; class Player:public Entity { }; class Enemy :public Entity { }; void Check(void* p) { if (p) { cout << "轉換成功" << endl; } else { cout << "轉換失敗" << endl; } } int main() { Entity* e = new Entity(); Entity* actuallyPlayer = new Player();//向上轉型 //安全的向下轉型 Player* player =(Player*)actuallyPlayer;//向下轉型需要轉換類型 Player* player2 = dynamic_cast<Player*>(actuallyPlayer); Check(player2); cout << endl; //不安全的向下轉型 Player* player3 = (Player*)e; Player* player4 = dynamic_cast<Player*>(e); Check(player4); cout << endl; Entity* actuallyEnemy = new Enemy(); Player* player5 = dynamic_cast<Player*>(actuallyEnemy); Check(player5); cout << endl; return 0; }
dynamic也常常利用返回值是否是空指針來判斷指針具體指向誰。
用dynamic_cast的地方其實也可以用static_cast,不過static_cast不會進行安全檢查,如果你很清楚其指向,并且在安全的前提下,為了減少程序開銷那可以考慮用static_cast
小結
- static_cast用于相關類型的轉換
- reinterpret_cast用于重新解釋內(nèi)存(很暴力,用的時候心里要有數(shù))
- const_cast常用來取出const屬性,常與指針連用來修改const變量的值
- dynamic_cast,在多態(tài)里提供安全的向下轉換(轉換不安全就返回空指針,只要對返回的指針進行判斷我們就能知道此次轉換安不安全了)
到此這篇關于C++實例講解四種類型轉換的使用的文章就介紹到這了,更多相關C++類型轉換內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
簡要對比C語言中的dup()函數(shù)和dup2()函數(shù)
這篇文章主要介紹了簡要對比C語言中的dup()函數(shù)和dup2()函數(shù),是C語言入門學習中的基礎知識,需要的朋友可以參考下2015-08-08