第41課 - 型別轉換函式(上)
1.再論型別轉換(隱式轉化和強制型別轉換)
(1)標準資料型別之間會進行隱式的型別安全轉換
(2)轉換規則:
8個位元組 long: 8個位元組 long long: 8個位元組 unsigned long: 8個位元組
小型別(佔用記憶體小)--->大型別(佔用記憶體大)-----隱式轉化(安全,不會發生程式的截斷和資料的丟失)
實驗:程式設計避免不同型別轉換,編譯器進行的隱式轉換很可怕
1 #include<iostream> 2 #include<string> 3 4 using namespace std; 5 6 intmain() 7 { 8 short s = 'a'; 9 unsigned int ui = 1000; //unsigned_-->int 10 int i = -2000; 11 double d = i; //小型別初始化大型別是安全的 12 13 cout << d << endl; //-2000 14 cout << ui<< endl; //1000 15 cout << ui+i << endl; //驗證:ui+i =4294966296 16 17//因為i會被轉為unsigned int型別,兩個無符號整形數相加,變成一個很大的正數。 18 //程式設計避免隱式型別轉換 19 20 if ((ui + i) > 0) //編譯器進行隱式型別轉換,將i(小型別)轉換為unsigned int(大型別) 21 { 22 cout << "positive" << endl; //negative 23 } 24 else 25 { 26 cout << "negative" << endl; 27 } 28 29 //根據標準資料型別的轉換規則s->int。'b'為char,也會被轉為int型。所以輸出4 30 31 cout << "sizeof(s+'b')=" << sizeof(s + 'b') << endl; //4 32 //編譯期將s和b都轉換為Int 33 return 0; 34 }
問題:
實驗:普通到類型別
1 #include<iostream> 2 #include<string> 3 4 using namespace std; 5 6 //類之間可不可以進行型別轉換 7 class Test 8 { 9 int mvalue; 10 public: 11 Test() 12 { 13 } 14 15 Test(int i) //轉換建構函式 16 { 17 mvalue = i; 18 } 19 Test operator + (const Test& p) 20 { 21 Test ret(mvalue + p.mvalue); 22 23 return ret; 24 } 25 26 int value() 27 { 28 return mvalue; 29 } 30 31 }; 32 int main() 33 { 34 Test t; 35 36 // t = (Test)5; //型別怎麼轉換???? 37 // t = Test(5); //直接呼叫建構函式,會產生一個臨時物件,臨時物件賦值給t物件,他倆屬於Test物件 38 //編譯器進行隱式型別轉換-----本質呼叫轉換建構函式 39 40 t = 5; //沒有轉化建構函式之前,錯誤 41 //有轉換建構函式定義,等價於t=Test(5); 正確 42 43 Test r; 44 r = t + 10; //相當於:r = t + Test(10); 手誤10整形,將2個Test物件相加,呼叫建構函式----error 45 46 cout << r.value() << endl; //15 47 48 return 0; 49 }
2.普通型別到類型別的轉換——轉換建構函式
也可以將其他類型別轉換到當前類型別
(1)建構函式可以定義不同型別的引數
(2)引數滿足下列條件時稱為轉換建構函式
①有且僅有一個引數
②引數是基本型別
③引數是其它類型別(但不是本類的const引用,因為那叫拷貝建構函式)
(3)另一個視角:舊式的C方式強制型別轉換
int i = int(1.5); //將浮點轉為整型,有點像函式呼叫 Test t;
t = Test(100); //老舊的C轉換方式,100整形強制型別轉換到類型別,本質---呼叫建構函式 有點像函式呼叫
但是這樣子隱式轉化是不好的,實驗說明:和上面一樣
Test r;
r = t + 10; //相當於:r = t + Test(10); 手誤10整形,將2個Test物件相加,呼叫建構函式----error
cout << r.value() << endl; //15
怎麼解決?????
3. explicit關鍵字-----
(1)編譯器隱式轉換行為:
①編譯器會盡力嘗試讓原始碼通過編譯
Test t; t = 100; //100這個立即數,預設為int型,怎麼可能賦值給t物件呢?現在就報錯 //嗎?不急,編譯器會看看有沒有轉換建構函式!Ok,發現Test類中定義 //Test(int i),可以進行轉換,預設等價於:t = Test(100);
②隱式型別轉換會讓程式以意想不到的方式進行工作,是工程中Bug的重要來源。
(2)杜絕編譯器的隱式型別轉換:explicit關鍵字
①用explicit修飾轉換建構函式。這時會阻止編譯器隱式地嘗試呼叫這個函式的行為。
②當轉換建構函式被explicit修飾時,只能手動進行顯式的轉換,轉換方式:
-
-
A.static_cast<ClassName>(value); //C++
-
B.ClassName(value); //C--手動呼叫建構函式
-
C.(ClassName)value; //不推薦
-
轉換建構函式之前加了explicit----編譯器不會自動呼叫轉換建構函式,需要手動呼叫
1 #include <iostream>
2
3 using namespace std;
4
5 class Test
6 {
7 int mValue;
8
9 public:
10
11 Test() { mValue = 0; }
12
13 explicit Test(int i) //轉換建構函式
14 {
15 mValue = i;
16 }
17
18 Test operator + (const Test& p)
19 {
20 Test ret(mValue + p.mValue);
21
22 return ret;
23 }
24
25 int value()
26 {
27 return mValue;
28 }
29
30 };
31
32
33
34 int main()
35 {
36 Test t;
37
38 //t = 5; //error,將5賦值給t物件,編譯器會嘗試呼叫Test(int i)轉換建構函式
39
40 //但由於Test(int i)前面用explicit修飾,以後拒絕了這種嘗試。所以
41
42 //編譯不通過。
43
44
45 t = static_cast<Test>(5); //相當於 t = Test(5); 原因:Test(int i)被explicit修飾,
46
47 //就是告訴編譯器要阻止那種通過隱式呼叫該函式的行為發生。
48
49 //只有顯式呼叫Test(int i)(或通過強制型別方式轉換的)
50
51 //才允許呼叫這個函式來進行型別的轉換。
52
53
54 Test r;
55
56 //r = t + 10; error
t=(Test)5; //c語言方式,不推薦
57
58 r = t + static_cast<Test>(10); //c++推薦寫法
59
60 cout << r.value() << endl; //15;
61
62 return 0;
63 }
4.小結
(1)轉換建構函式只有一個引數explicit Test(int i)
(2)轉換建構函式的引數型別是其它型別
(3)轉換建構函式在型別轉換時被呼叫
(4)隱式型別轉換是工程中Bug的重要來源
(5)explicit關鍵字用於杜絕編譯器這種隱式型別轉換的行為