C语言隐式/显式类型转换 | C++四种强制类型转换、类的隐式转换、explicit
文章目錄
- C語言類型轉(zhuǎn)換
- 隱式類型轉(zhuǎn)換
- 顯式類型轉(zhuǎn)換
- C++ 強制類型轉(zhuǎn)換
- static_cast
- reinterpret_cast
- const_cast
- dynamic_cast
- 類的隱式類型轉(zhuǎn)換
- 概念
- 只允許一步類類型轉(zhuǎn)換
- explicit 抑制構(gòu)造函數(shù)定義地隱式轉(zhuǎn)換
- 可以通過顯式轉(zhuǎn)換使用explicit構(gòu)造函數(shù)
C語言類型轉(zhuǎn)換
隱式類型轉(zhuǎn)換
編譯器在編譯階段自動進行,通常適用于相近的類型,如果不能轉(zhuǎn)換則會編譯失敗。
何時發(fā)生隱式類型轉(zhuǎn)換:
- 在大多數(shù)表達式中,比int類型小的整數(shù)值提升為較大的整數(shù)類型
- 在條件中,非布爾值轉(zhuǎn)換成布爾類型
- 初始化過程中,初始值轉(zhuǎn)換成變量的類型;在賦值語句中,右側(cè)運算對象轉(zhuǎn)化成左側(cè)運算對象的類型
- 算術(shù)運算或關(guān)系運算對象有多種類型,需要轉(zhuǎn)換成同一種類型
- 函數(shù)調(diào)用時也會發(fā)生類型轉(zhuǎn)換
顯式類型轉(zhuǎn)換
需要用戶自己處理,通常用于不相近類型的轉(zhuǎn)換。
int main() {int i = 9;int* p = &i;//將指針轉(zhuǎn)換為地址int addr = (int)p;cout << addr; }C語言的類型轉(zhuǎn)換使用起來很簡單,但是也有很大的缺點:
- 隱式類型轉(zhuǎn)換 可能會因為整形提升或者數(shù)據(jù)截斷導致 精度的丟失,并且有時候會因為 忽略隱式類型轉(zhuǎn)換導致錯誤發(fā)生 。
- 顯示類型轉(zhuǎn)換 代碼不夠清晰,沒有很好的將各種情況劃分開,而是全部混在一起使用。
C++ 強制類型轉(zhuǎn)換
C++為了加強類型轉(zhuǎn)換的可視性,引入了四種命名的強制類型轉(zhuǎn)換操作符:static_cast、reinterpret_cast、const_cast、dynamic_cast
static_cast
static_cast 用于非多態(tài)類型的轉(zhuǎn)換(靜態(tài)轉(zhuǎn)換),編譯器隱式執(zhí)行的任何類型轉(zhuǎn)換都可用 static_cast ,但它不能用于兩個不相關(guān)的類型進行轉(zhuǎn)換。(即對應C語言中的隱式類型轉(zhuǎn)換)
int main() {double d = 1.9;int i = static_cast<int>(d);cout << i; }reinterpret_cast
reinterpret_cast 是一種較為危險的類型轉(zhuǎn)換,通常為操作數(shù)的位模式提供較低層次的重新解釋,用于 將一種類型轉(zhuǎn)換為另一種不同的類型 ,通常適用于指針、引用、以及整數(shù)之間的類型轉(zhuǎn)換。
int main() {int i = 9;int* p = &i;double* p2 = reinterpret_cast<double*>(p);cout << *p2 << ' ' << *p; }
如上面所說,這種轉(zhuǎn)換十分容易導致錯誤的發(fā)生,因為指針類型其實是其指向的地址的類型,決定了指針看待這段地址的方式,它該如何讀取數(shù)據(jù)。這里我把 int 改成了 double,使得他原本應該讀取 4個字節(jié),而變成了 8個字節(jié),就導致了數(shù)據(jù)的變化。如果使用不當很容易會造成越界訪問導致程序崩潰。
const_cast
const_cast 通常用于刪除變量的const屬性。
如果想要修改 const變量的值,就需要用 volatile 來取消編譯器優(yōu)化。因為 const變量 創(chuàng)建后會被放入寄存器中,只有我們?nèi)?const變量 的地址時,他才會在內(nèi)存中申請空間。通常我們想要修改的是 const變量在內(nèi)存中的值 ,但是由于 編譯器優(yōu)化,當使用 const變量 時就會到 寄存器 中去取值,所以需要用 volatile 取消編譯器優(yōu)化,讓其每次在內(nèi)存中取值。
不加 volatile 時:
int main() {const int ci = 10;int* pi = const_cast<int*>(&ci); // 對應c語言強制類型轉(zhuǎn)換中去掉const屬性的(不相近類型)*pi = 20;cout << ci << ' ' << *pi; }
可以看出 ci 的 const屬性 仍在,其值并未更改,只改了 *pi 的值。
加 volatile 時:
int main() {volatile const int ci = 10;int* pi = const_cast<int*>(&ci); // 對應c語言強制類型轉(zhuǎn)換中去掉const屬性的(不相近類型)*pi = 20;cout << ci << ' ' << *pi; }dynamic_cast
dynamic_cast 是一種動態(tài)的類型轉(zhuǎn)換,是C++新增的概念,用于將一個 父類對象的指針/引用轉(zhuǎn)換為子類對象的指針/引用 。
派生類 可以賦值給 基類 的對象、指針或者引用,這樣的賦值也叫做對象切割。
例如Human類派生出的Student類:
當把 子類 賦值給 父類 時,可以通過切割掉多出來的成員 _stuNum 的方式來完成賦值。
但是 基類 對象如果想賦值給 派生類 ,則不可以,因為他不能憑空多一個 _stuNum 成員出來。
但是基類的 指針或者引用 卻可以強制類型轉(zhuǎn)換賦值給派生類對象。
- 這個過程有可能成功,也有可能會因為越界導致出現(xiàn)問題。 如果使用C語言的強制類型轉(zhuǎn)換,很可能就會出現(xiàn)問題,因為其沒有安全保障。
- 而如果使用 dynamic_cast ,則能夠保證安全,因為其會先檢查轉(zhuǎn)換是否能夠成功,如果不能成功則返回 0,能則直接轉(zhuǎn)換。但是 dynamic_cast 的向下轉(zhuǎn)換只支持繼承中的 多態(tài) 類型,也就是 父類之中必須包含虛函數(shù) 。
dynamic_cast 是如何識別父類的指針指向的是父類對象還是子類對象的呢?
其原理就是在運行時通過查找虛函數(shù)表上面的標識信息,來確認其指向的到底是父類還是子類,這也就是為什么只能用于含有虛函數(shù)的類。
這種在 運行中進行類型識別的方法 ,也叫做 RTTI ,C++中有很多支持 RTTI 的方法,如 dynamic_cast、typeid、decltype。
類的隱式類型轉(zhuǎn)換
概念
類的隱式類型轉(zhuǎn)換: 如果類有需要 一個實參 的構(gòu)造函數(shù)(或者構(gòu)造函數(shù)雖然有 N 個參數(shù),但這些參數(shù)都有缺省值),那么可以使用 該實參類型 構(gòu)造一個 具有常量屬性的臨時類。這樣的行為看起來像從 實參類型 到 類類型 的隱式轉(zhuǎn)換。
示例如下:
class People{int age;string name; public:People(int a){age = a;name = "zhangsan";}People(string b){age = 10;name = b;}People(){age = 21;name = "lihua";}People& add(const People &temp){age += temp.age;return *this;}People& revise(const People &temp){name = temp.name;return *this;}int getage(){return this->age;}string getname(){return this->name;} }; int main() {int num = 30;string who = "zhaoliu";People p1,p2(12);p1.add(num);cout << p1.getage() << endl;p2.revise(who);cout << p2.getname() << endl; }輸出結(jié)果:
在 People 類中,接收 int 和 string 的構(gòu)造函數(shù)分別運用了People類類型的隱式轉(zhuǎn)換機制(即分別定義了從 int 、string 類型向 People 隱式轉(zhuǎn)化的規(guī)則)。換言之,在需要People的地方,我們可以使用string或者int作為替代。
在上述代碼main函數(shù)中:
int num = 30; p1.add(num); // 用num構(gòu)造一個臨時的People對象,該對象的name為空串,age等于30我們用int實參充當了add的成員。該操作是合法的,編譯器用給定的int自動創(chuàng)建了一個People對象。新生成的這個臨時People對象被傳遞給add。因為add的參數(shù)是一個常量引用,所以我們可以給該參數(shù)傳遞一個臨時量。(因為臨時量具有常量屬性)
那如果add的參數(shù)不是常量引用,以上操作還可以進行嗎?
答案是否定的,因為num構(gòu)造臨時的people經(jīng)過了形如下式的轉(zhuǎn)化:
const People temp = People(num);之后是將temp臨時量作為實參傳入add函數(shù)
p1.add(temp)而如若add的形參只是一個普通引用的話,是無法綁定具有常量屬性的臨時量temp的。
只允許一步類類型轉(zhuǎn)換
編譯器只會自動地執(zhí)行一步類型轉(zhuǎn)換。
下面的調(diào)用是錯誤的:
p2.revise("huangba"); // error:隱式地使用了兩種轉(zhuǎn)換規(guī)則 // (1)把"huangba"轉(zhuǎn)換成string // (2)再把臨時的string轉(zhuǎn)換成People如果想完成上述調(diào)用,可以顯式地將字符串轉(zhuǎn)換為string或者People對象:
// 顯式地轉(zhuǎn)換成string,隱式地轉(zhuǎn)化成Peoplep2.revise(string("huangba"));// 顯式地轉(zhuǎn)換成People,隱式地轉(zhuǎn)化成stringp2.revise(People("huangba"));explicit 抑制構(gòu)造函數(shù)定義地隱式轉(zhuǎn)換
我們之前提到,如果構(gòu)造函數(shù)有多個參數(shù),但這些參數(shù)都有默認實參,那么同樣遵循類的隱式轉(zhuǎn)換規(guī)則:
class Date { public:Date(int year, int month = 4, int day = 24):_year(year),_month(month),_day(day){}int _year;int _month;int _day; };int main() {Date d1(2020, 4, 24);Date d2 = 2020;//C++98Date d3 = { 2020, 5 }; //C++11Date d4 = { 2020, 5, 26 }; //C+11 }這里其實是先用這個整型值來調(diào)用了缺省的構(gòu)造函數(shù)來創(chuàng)建了一個臨時對象,再使用這個對象來為 d2、d3、d4 賦值 。
如果想要禁止隱式轉(zhuǎn)換,可以通過將構(gòu)造函數(shù)聲明為 explicit 加以阻止:
此時,沒有任何構(gòu)造函數(shù)能用于隱式地創(chuàng)建 Date 對象,隱式轉(zhuǎn)換就不會發(fā)生了。
可以通過顯式轉(zhuǎn)換使用explicit構(gòu)造函數(shù)
編譯器不會將具有 explicit 屬性的構(gòu)造函數(shù)用于隱式轉(zhuǎn)換過程,但是我們可以顯式地進行轉(zhuǎn)換(強制類型轉(zhuǎn)換):
總結(jié)
以上是生活随笔為你收集整理的C语言隐式/显式类型转换 | C++四种强制类型转换、类的隐式转换、explicit的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 股指期货详细玩法 有以下4种
- 下一篇: 信用卡催收爆通讯录吗?遭遇暴力催收要学会