天天看点

C++隐式转换与explicit关键字隐式转换定义隐式转换的发生条件隐式转换的风险explicit的作用

隐式转换定义

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,这样可以大大减少错误的发生。

继续阅读