1. 防止类型收窄
(1)类型收窄:指的是导致数据内容发生变化或者精度丢失的隐式类型转换。
(2)类型收窄的几种情况:
①从浮点数隐式转换为整型数,如int i=2.2;
②从高精度浮点数隐式转换为低精度浮点数。如从long double转double或float。
③从整型数隐式转换为浮点数,并且超出浮点数的表示范围,如float=(unsigned long Long)-1,注意这表示将-1先强制转换为unsigned long long,再隐式转换为float。
④从整型数隐式转换为长度较短的整型数,并且超出其表示范围。如char x=65536。
(3)在C++98/03中,类型收窄编译器不会报错,而C++11中可以通过列表初始化来检查及防止类型收窄。
【编程实验】防止类型收窄
#include <iostream> #include <vector> using namespace std; int main() { int x1(5.3); //ok,x1=5 int x2 = 5.3; //ok,x2=5 int x3 {5.3}; //error:narrowing,gcc给的是warning int x4 = {5.3};//同上 char c1{7}; //ok char c2{99999};//error:narrowing,gcc给的是warning std::vector<int> v1{1,2,3,4,5}; //ok std::vector<int> v2{1,2,3,4,5.6}; //error:narrowing,gcc给的是warning float fa = 1e40; //ok,double->float float fb = {1e40};//error,double->float,超过float能表示的范围 float fc = (unsigned long long)-1; //将-1(0xFFFFFFFF)强转,再隐式转成float float fd = {(unsigned long long)-1}; //error float fe = (unsigned long long)1; //将1强转,再隐式转成float float ff = {(unsigned long long)1}; //ok const int x = 1024, y = 1; //注意x,y被const修饰 char c = x; //ok char d = {x};//error char e = y; //ok char f = {y};//ok。如果y为int型,则该行会出错。但由于加了const并且值为1。编译器 //认为这样的转换是安全的。 return 0; } /* *******************gcc上的测试结果********************* e:\Study\C++11\8>g++ -std=c++11 test1.cpp test1.cpp: In function 'int main()': test1.cpp:9:13: error: narrowing conversion of '5.2999999999999998e+0' from 'double' to 'int' inside { } [-Wnarrowing] test1.cpp:10:15: error: narrowing conversion of '5.2999999999999998e+0' from 'double' to 'int' inside { } [-Wnarrowing] test1.cpp:12:15: error: narrowing conversion of '99999' from 'int' to 'char' inside { } [-Wnarrowing] test1.cpp:14:33: error: narrowing conversion of '5.5999999999999996e+0' from 'double' to 'int' inside { } [-Wnarrowing] test1.cpp:17:18: error: narrowing conversion of '1.0e+40' from 'double' to 'float' inside { } [-Wnarrowing] test1.cpp:19:36: error: narrowing conversion of '18446744073709551615ull' from 'long long unsigned int' to 'float' inside { } [-Wnarrowing] test1.cpp:24:11: warning: overflow in implicit constant conversion [-Woverflow] test1.cpp:25:13: error: narrowing conversion of '1024' from 'int' to 'char' inside { } [-Wnarrowing] *******************vc2015上的测试结果********************* test1.cpp(9): error C2397: 从“double”转换到“int”需要收缩转换 test1.cpp(10): error C2397: 从“double”转换到“int”需要收缩转换 test1.cpp(12): error C2397: 从“int”转换到“char”需要收缩转换 test1.cpp(14): error C2398: 元素“5”: 从“double”转换到“int”需要收缩转换 test1.cpp(17): error C2397: 从“double”转换到“float”需要收缩转换 test1.cpp(19): error C2397: 从“unsigned __int64”转换到“float”需要收缩转换 test1.cpp(25): error C2397: 从“const int”转换到“char”需要收缩转换 */
2. explicit关键字
(1)explicit用于阻止编译器的隐式转换,一般用于修饰构造函数。
(2)C++98/03由于不能使用{}列表初始化,即隐式转换只发生在调用带一个参数的构造函数中。但C++11允许用{}列表初始化对象,这可能会隐式调用带多个参数的构造函数。
(3)C++11中,explicit可用于修饰带多个参数的构造函数以防止隐式转换。(注意C++98/03中explicit被用于修饰只带一个参数的构造函数,如果修饰带多个参数的构造函数则无效)
【编程实验】explicit修饰带多参的构造函数
1 #include <iostream> 2 using namespace std; 3 4 class Test 5 { 6 public: 7 Test(int a, int b) 8 { 9 cout <<"Test(int a, int b)" << endl; 10 } 11 12 Test(initializer_list<int>) 13 { 14 cout <<"Test(initializer_list<int>)" << endl; 15 } 16 17 explicit Test(int a, int b, int c) 18 { 19 cout <<"explicit Test(int a, int b, int c)" << endl; 20 } 21 }; 22 23 void func(const Test& t) 24 { 25 26 } 27 28 struct Complex 29 { 30 int real, imag; 31 32 //explicit 33 Complex(int re, int im = 0):real(re), imag(im){} 34 35 Complex operator+(const Complex& x) 36 { 37 return Complex((real+x.real),(imag+x.imag)); 38 } 39 }; 40 41 int main() 42 { 43 Complex c1(12, 5); 44 Complex c2 = c1 + 5;//会试图将5转换为Complex类型,所以会隐式调用构造 45 //函数。为了阻止这种行为,可以在构造函数前加explicit 46 47 //1.由于Test带有一个initializer_list参数的构造函数,因此,下列 48 //用{}初始化的对象都会直接调用该构造函数。 49 //2.如果注释掉上述构造函数,则编译器会将{...}分解并传给相应的 50 //构造函数,如果找不到相应的带多参的构造函数,则直接报错。 51 Test t1(77, 5); //Test(int a, int b) 52 Test t2{77, 5}; //Test(initializer_list<int>) 53 Test t3{77, 5, 42}; //Test(initializer_list<int>) 54 Test t4 = {77, 5}; //Test(initializer_list<int>) 55 Test t5 = {77, 5, 42};//Test(initializer_list<int>) 56 Test t6(77, 5, 42); //ok,显式调用explicit Test(int a, int b, int c) 57 58 func({47, 11}); //Test(initializer_list<int>) 59 func({47, 11, 3}); //Test(initializer_list<int>) 60 func(Test{47, 11}); //Test(initializer_list<int>) 61 func(Test{47, 11, 3}); //Test(initializer_list<int>) 62 63 Test t11{77, 5, 42, 500}; //Test(initializer_list<int>) 64 Test t12 = {77, 5, 42, 500};//Test(initializer_list<int>) 65 Test t13 {10}; //Test(initializer_list<int>) 66 67 return 0; 68 } 69 /*测试结果: 70 **************************不注释Test中带有一个initializer_list参数的构造函数*************************** 71 e:\Study\C++11\8>g++ -std=c++11 test2.cpp 72 e:\Study\C++11\8>a.exe 73 Test(int a, int b) 74 Test(initializer_list<int>) 75 Test(initializer_list<int>) 76 Test(initializer_list<int>) 77 Test(initializer_list<int>) 78 explicit Test(int a, int b, int c) 79 Test(initializer_list<int>) 80 Test(initializer_list<int>) 81 Test(initializer_list<int>) 82 Test(initializer_list<int>) 83 Test(initializer_list<int>) 84 Test(initializer_list<int>) 85 Test(initializer_list<int>) 86 87 **************************不注释Test中带有一个initializer_list参数的构造函数*************************** 88 e:\Study\C++11\8>g++ -std=c++11 test2.cpp -fno-elide-constructors 89 test2.cpp: In function 'int main()': 90 test2.cpp:55:22: error: converting to 'Test' from initializer list would use explicit constructor 'Test::Test(int, int, int)' 91 Test t5 = {77, 5, 42}; 92 93 test2.cpp:59:18: error: converting to 'const Test' from initializer list would use explicit constructor 'Test::Test(int, int, int)' 94 func({47, 11, 3}); 95 96 test2.cpp:63:25: error: no matching function for call to 'Test::Test(<brace-enclosed initializer list>)' 97 Test t11{77, 5, 42, 500}; 98 99 test2.cpp:64:28: error: could not convert '{77, 5, 42, 500}' from '<brace-enclosed initializer list>' to 'Test' 100 Test t12 = {77, 5, 42, 500}; 101 102 test2.cpp:65:14: error: no matching function for call to 'Test::Test(<brace-enclosed initializer list>)' 103 Test t13 {10}; 104 */