日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

C++ 四种类型转换运算符

發布時間:2025/3/12 c/c++ 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C++ 四种类型转换运算符 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

隱式類型轉換是安全的,顯式類型轉換是有風險的,C語言之所以增加強制類型轉換的語法,就是為了強調風險,讓程序員意識到自己在做什么。

但是,這種強調風險的方式還是比較粗放,粒度比較大,它并沒有表明存在什么風險,風險程度如何。再者,C風格的強制類型轉換統一使用( ),而( )在代碼中隨處可見,所以也不利于使用文本檢索工具定位關鍵代碼。

為了使潛在風險更加細化,使問題追溯更加方便,使書寫格式更加規范,C++ 對類型轉換進行了分類,并新增了四個關鍵字來予以支持,它們分別是:

關鍵字說明
static_cast用于良性轉換,一般不會導致意外發生,風險很低。
const_cast用于 const 與非 const、volatile 與非 volatile 之間的轉換。
reinterpret_cast高度危險的轉換,這種轉換僅僅是對二進制位的重新解釋,不會借助已有的轉換規則對數據進行調整,但是可以實現最靈活的 C++ 類型轉換。
dynamic_cast借助 RTTI,用于類型安全的向下轉型(Downcasting)。

這四個關鍵字的語法格式都是一樣的,具體為:

xxx_cast<newType>(data)

newType 是要轉換成的新類型,data 是被轉換的數據。C語言風格的 double 轉 int 的寫法為:

double scores = 95.5; int n = (int)scores;

C++ 風格的寫法為:

double scores = 95.5; int n = static_cast<int>(scores);

static_cast 關鍵字

static_cast 只能用于良性轉換,這樣的轉換風險較低,一般不會發生什么意外,例如:

原有的自動類型轉換,例如 short 轉 int、int 轉 double、const 轉非 const、向上轉型等;void 指針和具體類型指針之間的轉換,例如void *轉int *、char *轉void *等;有轉換構造函數或者類型轉換函數的類與其它類型之間的轉換,例如 double 轉 Complex(調用轉換構造函數)、Complex 轉 double(調用類型轉換函數)。

注意:static_cast 不能用于無關類型之間的轉換,因為這些轉換都是有風險的,例如:

1 . 兩個具體類型指針之間的轉換,例如int *轉double *、Student *轉int *等。不同類型的數據存儲格式不一樣,長度也不一樣,用 A 類型的指針指向 B 類型的數據后,會按照 A 類型的方式來處理數據:如果是讀取操作,可能會得到一堆沒有意義的值;如果是寫入操作,可能會使 B 類型的數據遭到破壞,當再次以 B 類型的方式讀取數據時會得到一堆沒有意義的值。

2 . int 和指針之間的轉換。將一個具體的地址賦值給指針變量是非常危險的,因為該地址上的內存可能沒有分配,也可能沒有讀寫權限,恰好是可用內存反而是小概率事件。

static_cast 也不能用來去掉表達式的 const 修飾和 volatile 修飾。換句話說,不能將 const/volatile 類型轉換為非 const/volatile 類型。

static_cast 是“靜態轉換”的意思,也就是在編譯期間轉換,轉換失敗的話會拋出一個編譯錯誤。

static_cast 的正確用法:

#include <iostream> #include <cstdlib> using namespace std;class Complex{ public:Complex(double real = 0.0, double imag = 0.0): m_real(real), m_imag(imag){ } public:operator double() const { return m_real; } //類型轉換函數 private:double m_real;double m_imag; };int main(){//下面是正確的用法int m = 100;Complex c(12.5, 23.8);long n = static_cast<long>(m); //寬轉換,沒有信息丟失char ch = static_cast<char>(m); //窄轉換,可能會丟失信息int *p1 = static_cast<int*>( malloc(10 * sizeof(int)) ); //將void指針轉換為具體類型指針void *p2 = static_cast<void*>(p1); //將具體類型指針,轉換為void指針double real= static_cast<double>(c); //調用類型轉換函數return 0; }

const_cast 關鍵字

const_cast 比較好理解,它用來去掉表達式的 const 修飾或 volatile 修飾。換句話說,const_cast 就是用來將 const/volatile 類型轉換為非 const/volatile 類型。

下面我們以 const 為例來說明 const_cast 的用法:

#include <iostream> using namespace std;int main(){const int n = 100;int *p = const_cast<int*>(&n);*p = 234;cout<<"n = "<<n<<endl;cout<<"*p = "<<*p<<endl;return 0; }

運行結果:

n = 100 *p = 234

&n用來獲取 n 的地址,它的類型為const int *,必須使用 const_cast 轉換為int *類型后才能賦值給 p。由于 p 指向了 n,并且 n 占用的是棧內存,有寫入權限,所以可以通過 p 修改 n 的值。

有人可能會問,為什么通過 n 和 *p 輸出的值不一樣呢?這是因為 C++ 對常量的處理更像是編譯時期的#define,是一個值替換的過程,代碼中所有使用 n 的地方在編譯期間就被替換成了 100。換句話說,第 8 行代碼被修改成了下面的形式:

cout<<"n = "<<100<<endl;

這樣以來,即使程序在運行期間修改 n 的值,也不會影響 cout 語句了。

使用 const_cast 進行強制類型轉換可以突破 C/C++ 的常數限制,修改常數的值,因此有一定的危險性;

reinterpret_cast 關鍵字

reinterpret 是“重新解釋”的意思,顧名思義,reinterpret_cast 這種轉換僅僅是對二進制位的重新解釋,不會借助已有的轉換規則對數據進行調整,非常簡單粗暴,所以風險很高。

reinterpret_cast 可以認為是 static_cast 的一種補充,一些 static_cast 不能完成的轉換,就可以用 reinterpret_cast 來完成,例如兩個具體類型指針之間的轉換、int 和指針之間的轉換(有些編譯器只允許 int 轉指針,不允許反過來)。

下面的代碼代碼演示了 reinterpret_cast 的使用:

#include <iostream> using namespace std;class A{ public:A(int a = 0, int b = 0): m_a(a), m_b(b){} private:int m_a;int m_b; };int main(){//將 char* 轉換為 float*char str[]="http://c.biancheng.net";float *p1 = reinterpret_cast<float*>(str);cout<<*p1<<endl;//int 轉換為 int*int *p = reinterpret_cast<int*>(100);//將 A* 轉換為 int*p = reinterpret_cast<int*>(new A(25, 96));cout<<*p<<endl;return 0; }

運行結果:

3.0262e+29 25

可以想象,用一個 float 指針來操作一個 char 數組是一件多么荒誕和危險的事情,這樣的轉換方式不到萬不得已的時候不要使用。將A轉換為int,使用指針直接訪問 private 成員刺穿了一個類的封裝性,更好的辦法是讓類提供 get/set 函數,間接地訪問成員變量。

dynamic_cast 關鍵字

dynamic_cast 用于在類的繼承層次之間進行類型轉換,它既允許向上轉型(Upcasting),也允許向下轉型(Downcasting)。向上轉型是無條件的,不會進行任何檢測,所以都能成功;向下轉型的前提必須是安全的,要借助 RTTI 進行檢測,所有只有一部分能成功。

dynamic_cast 與 static_cast 是相對的,dynamic_cast 是“動態轉換”的意思,static_cast 是“靜態轉換”的意思。dynamic_cast 會在程序運行期間借助 RTTI 進行類型轉換,這就要求基類必須包含虛函數;static_cast 在編譯期間完成類型轉換,能夠更加及時地發現錯誤。

dynamic_cast 的語法格式為:

dynamic_cast <newType> (expression)

newType 和 expression 必須同時是指針類型或者引用類型。換句話說,dynamic_cast 只能轉換指針類型和引用類型,其它類型(int、double、數組、類、結構體等)都不行。

對于指針,如果轉換失敗將返回 NULL;對于引用,如果轉換失敗將拋出std::bad_cast異常。

1 向上轉型(Upcasting)
向上轉型時,只要待轉換的兩個類型之間存在繼承關系,并且基類包含了虛函數(這些信息在編譯期間就能確定),就一定能轉換成功。因為向上轉型始終是安全的,所以 dynamic_cast 不會進行任何運行期間的檢查,這個時候的 dynamic_cast 和 static_cast 就沒有什么區別了。

「向上轉型時不執行運行期檢測」雖然提高了效率,但也留下了安全隱患,請看下面的代碼:

#include <iostream> #include <iomanip> using namespace std;class Base{ public:Base(int a = 0): m_a(a){ }int get_a() const{ return m_a; }virtual void func() const { } protected:int m_a; };class Derived: public Base{ public:Derived(int a = 0, int b = 0): Base(a), m_b(b){ }int get_b() const { return m_b; } private:int m_b; };int main(){//情況①Derived *pd1 = new Derived(35, 78);Base *pb1 = dynamic_cast<Derived*>(pd1);cout<<"pd1 = "<<pd1<<", pb1 = "<<pb1<<endl;cout<<pb1->get_a()<<endl;pb1->func();//情況②int n = 100;Derived *pd2 = reinterpret_cast<Derived*>(&n);Base *pb2 = dynamic_cast<Base*>(pd2);cout<<"pd2 = "<<pd2<<", pb2 = "<<pb2<<endl;cout<<pb2->get_a()<<endl; //輸出一個垃圾值pb2->func(); //內存錯誤return 0; }

情況①是正確的,沒有任何問題。對于情況②,pd 指向的是整型變量 n,并沒有指向一個 Derived 類的對象,在使用 dynamic_cast 進行類型轉換時也沒有檢查這一點,而是將 pd 的值直接賦給了 pb(這里并不需要調整偏移量),最終導致 pb 也指向了 n。因為 pb 指向的不是一個對象,所以get_a()得不到 m_a 的值(實際上得到的是一個垃圾值),pb2->func()也得不到 func() 函數的正確地址。

pb2->func()得不到 func() 的正確地址的原因在于,pb2 指向的是一個假的“對象”,它沒有虛函數表,也沒有虛函數表指針,而 func() 是虛函數,必須到虛函數表中才能找到它的地址。

2 向下轉型(Downcasting)
向下轉型是有風險的,dynamic_cast 會借助 RTTI 信息進行檢測,確定安全的才能轉換成功,否則就轉換失敗。那么,哪些向下轉型是安全地呢,哪些又是不安全的呢?下面我們通過一個例子來演示:

#include <iostream> using namespace std;class A{ public:virtual void func() const { cout<<"Class A"<<endl; } private:int m_a; };class B: public A{ public:virtual void func() const { cout<<"Class B"<<endl; } private:int m_b; };class C: public B{ public:virtual void func() const { cout<<"Class C"<<endl; } private:int m_c; };class D: public C{ public:virtual void func() const { cout<<"Class D"<<endl; } private:int m_d; };int main(){A *pa = new A();B *pb;C *pc;//情況①pb = dynamic_cast<B*>(pa); //向下轉型失敗if(pb == NULL){cout<<"Downcasting failed: A* to B*"<<endl;}else{cout<<"Downcasting successfully: A* to B*"<<endl;pb -> func();}pc = dynamic_cast<C*>(pa); //向下轉型失敗if(pc == NULL){cout<<"Downcasting failed: A* to C*"<<endl;}else{cout<<"Downcasting successfully: A* to C*"<<endl;pc -> func();}cout<<"-------------------------"<<endl;//情況②pa = new D(); //向上轉型都是允許的pb = dynamic_cast<B*>(pa); //向下轉型成功if(pb == NULL){cout<<"Downcasting failed: A* to B*"<<endl;}else{cout<<"Downcasting successfully: A* to B*"<<endl;pb -> func();}pc = dynamic_cast<C*>(pa); //向下轉型成功if(pc == NULL){cout<<"Downcasting failed: A* to C*"<<endl;}else{cout<<"Downcasting successfully: A* to C*"<<endl;pc -> func();}return 0; }

運行結果:

Downcasting failed: A* to B* Downcasting failed: A* to C* ------------------------- Downcasting successfully: A* to B* Class D Downcasting successfully: A* to C* Class D

這段代碼中類的繼承順序為:A --> B --> C --> D。pa 是A類型的指針,當 pa 指向 A 類型的對象時,向下轉型失敗,pa 不能轉換為B或C類型。當 pa 指向 D 類型的對象時,向下轉型成功,pa 可以轉換為B或C*類型。同樣都是向下轉型,為什么 pa 指向的對象不同,轉換的結果就大相徑庭呢?

編譯器會將存在繼承關系的類的類型信息使用指針“連接”起來,從而形成一個繼承鏈(Inheritance Chain),也就是如下圖所示的樣子:

當使用 dynamic_cast 對指針進行類型轉換時,程序會先找到該指針指向的對象,再根據對象找到當前類(指針指向的對象所屬的類)的類型信息,并從此節點開始沿著繼承鏈向上遍歷,如果找到了要轉化的目標類型,那么說明這種轉換是安全的,就能夠轉換成功,如果沒有找到要轉換的目標類型,那么說明這種轉換存在較大的風險,就不能轉換。

總結

以上是生活随笔為你收集整理的C++ 四种类型转换运算符的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。