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 int main() 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关键字用于杜绝编译器这种隐式类型转换的行为