隐式转换定义
Implicit conversions are performed whenever an expression of some type T1 is used in context that does not accept that type, but accepts some other type T2; in particular:
- when the expression is used as the argument when calling a function that is declared with T2 as parameter;
- when the expression is used as an operand with an operator that expects T2;
- when initializing a new object of type T2, including return statement in a function returning T2;
- when the expression is used in a switch statement (T2 is integral type);
- when the expression is used in an if statement or a loop (T2 is bool).
所谓隐式转换,是指不需要用户干预,编译器私下进行的类型转换行为。很多时候用户可能都不知道进行了哪些转换。
隐式转换的发生条件
这里只讨论自定义的类的隐式转换发生条件。对于基本数据类型,隐式转换发生在取值范围从小->大,低精度->高精度的转换中。
按照默认规定,只要有一个构造函数,C++就会为这个构造函数定义一个隐式转换,将该构造函数对应数据类型的数据转换为该类对象。
例子:
class String{
public:
int n;
char * p;
String(int n){ this->n = n; }
String (int a, int b, int c){
this->n = a+b+c;
}
};
String s1 = 10; //可以:隐式转换调用String(int n);再调用默认的复制构造函数
String s10 = {1,2,3}; //可以:隐式转换调用String(int a, int b, int c);
//再调用默认的复制构造函数
隐式转换的风险
隐式转换的风险也一般存在于自定义的类构造函数中。
例子1:
class String
{
public:
String ( int n ); //本意是预先分配n个字节给字符串
String ( const char* p ); // 用C风格的字符串p作为初始化值
//…
}
下面两种写法比较正常:
String s2 ( 10 ); //OK 分配10个字节的空字符串
String s3 = String ( 10 ); //OK 分配10个字节的空字符串
下面两种写法就比较疑惑了:
String s4 = 10; //编译通过,也是分配10个字节的空字符串
String s5 = ‘a’; //编译通过,分配int(‘a’)个字节的空字符串
s4 和s5 分别把一个int型和char型,隐式转换成了分配若干字节的空字符串,容易令人误解。
explicit
的作用
==通过将构造函数声明为explicit(显式)的方式可以抑制隐式转换。==也就是说,explicit构造函数必须显式调用。
class String{
public:
int n;
char * p;
explicit String(int n){ this->n = n; }
String ( const char* p );
explicit String(int a, int b, int c){
this->n = a+b+c;
}
};
String s1 = 10; ///错误:不能做隐式int->String转换
String s2(10); //可以:调用explicit String(int n);
String s3 = String(10);//可以:调用explicit String(int n);再调用默认的复制构造函数
String s4('a'); //可以:char->int 调用explicit String(int n);
//再调用默认的复制构造函数
String s5 = {1,2,3}; //错误:不能调用隐式转换String(int a, int b, int c);
String s6(1,2,3); //可以:调用String(int a, int b, int c);
//再调用默认的复制构造函数
String s7 = "Brian"; //可以:隐式转换调用String(const char *p);
//再调用默认的复制构造函数
隐式转换常常带来程序逻辑的错误,而且这种错误一旦发生是很难察觉的。
原则上应该在所有的构造函数前加explicit关键字,当你有心利用隐式转换的时候再去解除explicit,这样可以大大减少错误的发生。