1.實現隱式類類型轉換
short a=2000;
int b;
b=a;
short是兩字節(jié),int是四字節(jié)镶蹋,由short型轉成int型是寬化轉換(bit位數增多)成艘,編譯器沒有warning赏半,如下圖所示。寬化轉換(如char到int淆两,int到long long断箫,int到float,float到double秋冰,int到double等)構成隱式轉換仲义,編譯器允許直接轉換。
但若
double a=2000;
short b;
b=a;
是從8字節(jié)的double型轉成2字節(jié)的short型變量剑勾,是窄化轉換埃撵,編譯器就會有warning了,如下所示甥材,提醒程序員可能丟失數據盯另。不過需要注意的是,有些隱式轉換洲赵,編譯器可能并不給出warning鸳惯,比如int到short,但數據溢出卻依然會發(fā)生叠萍。
隱式轉換一
使用單參數的構造函數或N個參數中有N-1個是默認參數的構造函數芝发,如:
class A
{
public:
? ? ? A(stirng s);
? ? ? A(string s,int a = 0);
};
class String?
{?
public:?
? ? String ( const char* p ); // 用C風格的字符串p作為初始化值?
? ? //…?
}?
String s1 = “hello”; //OK 隱式轉換,等價于String s1 = String(”hello”)苛谷,將char型變成了string類
隱式轉換二
使用operator what_you_want_to_convert_type() const
class A
? ? ? ? {
? ? ? ? public:
? ? ? ? ? ? ? ? operator char*() const
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? return data;//當從其他類型轉換到char*時自動調用
? ? ? ? ? ? ? ? }
? ? ? ? private:
? ? ? ? ? ? ? ? char* data;
? ? ? ? };
避免隱式類型轉換的方法:在單參數的構造函數或N個參數中有N-1個是默認參數的構造函數聲明之前加上explicit辅鲸。
C風格顯式轉換(C style explicit conversion)
要去掉上述waring很簡單,熟悉C語言的程序員知道腹殿,有兩種簡單的寫法(C風格轉換與函數風格轉換):
double a=2000.3;
short b;
b = (short) a;? ? // c-like cast notation
b = short (a);? ? // functional notation
// class type-casting
#include <iostream>
using namespace std;
class CDummy {
float i,j;
CDummy():i(100),j(10){}
};
class CAddition:public CDummy
{
? ? int *x,y;
? public:
? ? CAddition (int a, int b) { x=&a; y=b; }
? ? int result() { return *x+y;}
};
int main () {
? CDummy d;
? CAddition * padd;
? padd = (CAddition*) &d;
? cout << padd->result();
? return 0;
}
編譯器不報任何錯独悴,但運行結果出錯,
交流群728483370锣尉,一起學習加油~每天晚上群里面也會有大佬直播企業(yè)項目開發(fā)學習刻炒,更有各種項目案例供你自己學習哦!
此時父類的指針&d被C風格轉換方式強制轉成了子類的指針了,后面調用了子類的方法result自沧,需要訪問*x坟奥,但指針指向的對象本質還是父類的,所以x相當于父類中的i拇厢,y相當于父類中的j爱谁,*x相當于*i,但i是float型變量(初始化為100)孝偎,不是地址访敌,所以出錯,如果程序員正是魯莽地對這個地址指向的內存進行寫入操作衣盾,那將可能會破壞系統(tǒng)程序捐顷,導致操作系統(tǒng)崩潰荡陷!
這里有一個重要概念,CAddition*是子類的指針迅涮,它的變量padd可以調用子類的方法废赞,但是它指向的是父類的對象,也就是說padd指向的內存空間里存放的是父類的成員變量叮姑。深入地說唉地,數據在內存中是沒有“類型”一說的,比如0x3F可能是字符型传透,也可能是整型的一部分耘沼,還可能是地址的一部分。我們定義的變量類型朱盐,其實就是定義了數據應該“被看成什么”的方式群嗤。
因此padd類指針實質是定義了取值的方式,如padd->x就是一并取出內存空間里的0號單元至3號單元的值(共4個字節(jié))兵琳,將其拼成32位并當作指針狂秘,padd->y則取出內存空間里的4號單元至7號單元(共4個字節(jié)),將其拼成32位并當作int型變量躯肌。但實際上padd指向的是父類的對象者春,也就是前4個字節(jié)是float型變量,后4個字節(jié)也是float型變量清女。
從這里可以看出钱烟,程序員的這種轉換使編譯器“理解”出錯,把牛當成馬了嫡丙。
從上可見拴袭,用C風格的轉換其實是不安全的,編譯器無法看到轉換的不安全曙博。
上行轉換(up-casting)與下行轉換(down-casting)
看到這個拥刻,讀者可能會問,哪些轉換不安全羊瘩?根據前面所舉的例子,可以看到盼砍,不安全來源于兩個方面:其一是類型的窄化轉化尘吗,會導致數據位數的丟失;其二是在類繼承鏈中浇坐,將父類對象的地址(指針)強制轉化成子類的地址(指針)睬捶,這就是所謂的下行轉換〗酰“下”表示沿著繼承鏈向下走(向子類的方向走)擒贸。
類似地臀晃,上行轉換的“上”表示沿繼承鏈向上走(向父類的方向走)。
我們給出結論介劫,上行轉換一般是安全的徽惋,下行轉換很可能是不安全的。
為什么呢座韵?因為子類中包含父類险绘,所以上行轉換(只能調用父類的方法,引用父類的成員變量)一般是安全的誉碴。但父類中卻沒有子類的任何信息宦棺,而下行轉換會調用到子類的方法、引用子類的成員變量黔帕,這些父類都沒有代咸,所以很容易“指鹿為馬”或者干脆指向不存在的內存空間。
值得一說的是成黄,不安全的轉換不一定會導致程序出錯呐芥,比如一些窄化轉換在很多場合都會被頻繁地使用,前提是程序員足夠小心以防止數據溢出慨默;下行轉換關鍵看其“本質”是什么贩耐,比如一個父類指針指向子類,再將這個父類指針轉成子類指針厦取,這種下行轉換就不會有問題潮太。
針對類指針的問題,C++特別設計了更加細致的轉換方法虾攻,分別有:
static_cast <new_type> (expression)
dynamic_cast <new_type> (expression)
reinterpret_cast <new_type> (expression)
const_cast <new_type> (expression)
可以提升轉換的安全性铡买。
static_cast (expression) 靜態(tài)轉換
靜態(tài)轉換是最接近于C風格轉換,很多時候都需要程序員自身去判斷轉換是否安全霎箍。比如:
double d=3.14159265;
int i = static_cast(d)奇钞;
但static_cast已經有安全性的考慮了,比如對于不相關類指針之間的轉換漂坏。參見下面的例子:
// class type-casting
#include <iostream>
using namespace std;
class CDummy {
? ? float i,j;
};
class CAddition {
? ? int x,y;
? public:
? ? CAddition (int a, int b) { x=a; y=b; }
? ? int result() { return x+y;}
};
int main () {
? CDummy d;
? CAddition * padd;
? padd = (CAddition*) &d;
? cout << padd->result();
? return 0;
}
這個例子與之前舉的例子很像景埃,只是CAddition與CDummy類沒有任何關系了,但main()中C風格的轉換仍是允許的padd = (CAddition*) &d顶别,這樣的轉換沒有安全性可言谷徙。
如果在main()中使用static_cast,像這樣:
int main () {
? CDummy d;
? CAddition * padd;
? padd = static_cast<CAddition*> (&d);
? cout << padd->result();
? return 0;
}
編譯器就能看到這種不相關類指針轉換的不安全驯绎,報出錯誤完慧。注意這時不是以warning形式給出的,而直接是不可通過編譯的error剩失。從提示信息里可以看到屈尼,編譯器說如果需要這種強制轉換册着,要使用reinterpret_cast(稍候會說)或者C風格的兩種轉換。
總結一下:static_cast最接近于C風格轉換了脾歧,但在無關類的類指針之間轉換上甲捏,有安全性的提升。
dynamic_cast (expression) 動態(tài)轉換
動態(tài)轉換確保類指針的轉換是合適完整的涨椒,它有兩個重要的約束條件摊鸡,
要求new_type為指針或引用
下行轉換時要求基類是多態(tài)的(基類中包含至少一個虛函數)。
看一下下面的例子:
#include <iostream>
using namespace std;
class CBase { };
class CDerived: public CBase { };
int main()
{
CBase b; CBase* pb;
CDerived d; CDerived* pd;
pb = dynamic_cast<CBase*>(&d);? ? // ok: derived-to-base
pd = dynamic_cast<CDerived*>(&b);? // wrong: base-to-derived
}
在最后一行代碼有問題蚕冬,編譯器給的錯誤提示免猾,基類CBase不是vritual多態(tài)類型。
把類的定義改成:
class CBase { virtual void dummy() {} };
class CDerived: public CBase {};
再編譯囤热,結果編譯成功猎提。
我們看到一個奇怪的現象,將父類經過dynamic_cast轉成子類的指針竟然是空指針旁蔼!這正是dynamic_cast提升安全性的功能锨苏,dynamic_cast可以識別出不安全的下行轉換,但并不拋出異常棺聊,而是將轉換的結果設置成null(空指針)伞租。
再舉一個例子:
#include <iostream>
#include <exception>
using namespace std;
class CBase { virtual void dummy() {} };
class CDerived: public CBase { int a; };
int main () {
? try {
? ? CBase * pba = new CDerived;
? ? CBase * pbb = new CBase;
? ? CDerived * pd;
? ? pd = dynamic_cast<CDerived*>(pba);
? ? if (pd==0) cout << "Null pointer on first type-cast" << endl;
? ? pd = dynamic_cast<CDerived*>(pbb);
? ? if (pd==0) cout << "Null pointer on second type-cast" << endl;
? } catch (exception& e) {cout << "Exception: " << e.what();}
? return 0;
}
輸出結果是:Null pointer on second type-cast
兩個dynamic_cast都是下行轉換,第一個轉換是安全的限佩,因為指向對象的本質是子類葵诈,轉換的結果使子類指針指向子類,天經地義祟同;第二個轉換是不安全的作喘,因為指向對象的本質是父類,“指鹿為馬”或指向不存在的空間很可能發(fā)生晕城!
reinterpret_cast (expression) 重解釋轉換
這個轉換是最“不安全”的泞坦,兩個沒有任何關系的類指針之間轉換都可以用這個轉換實現
class A {};
class B {};
A * a = new A;
B * b = reinterpret_cast<B*>(a);//correct!
reinterpret_cast可以把整型數轉換成地址(指針),這種轉換在系統(tǒng)底層的操作砖顷,有極強的平臺依賴性贰锁,移植性不好。
它同樣要求new_type是指針或引用滤蝠,下面的例子是通不過編譯的:
double a=2000.3;
short b;
b = reinterpret_cast (a); //compile error!
const_cast (expression) 常量向非常量轉換
// const_cast
#include <iostream>
using namespace std;
void print (char * str)
{
? cout << str << endl;
}
int main () {
? const char * c = "sample text";
? char *cc = const_cast<char *> (c) ;
? Print(cc);
? return 0;
}
交流群728483370豌熄,一起學習加油~每天晚上群里面也會有大佬直播企業(yè)項目開發(fā)學習,更有各種項目案例供你自己學習哦!
從char *cc = const_cast<char *>(c)可以看出了這個轉換的作用了几睛,但切記房轿,這個轉換并不轉換原常量本身粤攒,即c還是常量所森,只是它返回的結果cc是非常量了囱持。