c++中的explicit关键字用来并且只能用来修饰类的构造函数,表明该构造函数是显式的,“显式”的意思是说创建类对象时必须写出构造函数的名称,相对的,“隐式”指的是创建类对象时不必写出构造函数名,只需根据构造函数形参类型指定实参,由编译器进行形参类型到类类型的隐式转换。

C++ Primer》指出:可以用单个实参来调用的构造函数,它自身定义了从形参类型到类类型的一个隐式转换。

也就是说,如果c++类的构造函数可以只传一个实参来调用(构造函数的形式参数实际上可能不止一个,但除了传入实参的那个参数外,其他的参数都有默认值),那么在编译的时候就会有一个缺省的转换操作:将该构造函数对应数据类型的数据转换为该类对象,如下面所示:
class ACEClass
{
public:
   ACEClass( int age );

ACEClass(const std::string &name);

ACEClass(std::istream &is);
}
....

ACEClass obj(20); //ok, 显式调用构造函数
ACEClass obj2 = 20; //ok,
隐式转换,将int转换成ACEClass

String nullName = null;

ACEClass aceName = nullName;         //ok, 隐式转换,将string转换成ACEClass

ACEClass aceStream = cin;                     //ok, 隐式转换,将std::istream转换成ACEClass
在上面的代码中编译器自动将整型转换为ACEClass类对象,实际上等同于下面的操作:

//编译器使用一个接受intACEClass构造函数从20生成一个新的ACEClass对象temp
ACEClass temp(20);      

//新生成的临时对象temp被传递给obj2
ACEClass obj2 = temp;
上面的所有的操作即是所谓的"隐式转换"


有时这种隐式转换会给代码阅读者造成不必要的困惑,因此,很多时候我们要避免编译器这种自动转换的功能,这时就用到了文章开头提及的关键字explicit了。将类的构造函数声明为"显式",也就是在声明构造函数的时候前面添加上explicit即可,这样就可以防止这种自动的转换操作,如果我们修改上面的ACEClass类的构造函数为显式的,那么下面的代码就不能够编译通过了,如下所示:
class ACEClass
{
public:
   explicit ACEClass( int num );

explicit ACEClass(const std::string &name);

explicit ACEClass(std::istream &is);

...
}
....

ACEClass obj(20); //ok,显式调用构造函数
ACEClass obj 2= 20; //err,
不能进行隐式转换

String nullName = null;

ACEClass aceName = nullName; // err,不能进行隐式转换

ACEClass aceStream = cin; // err,不能进行隐式转换


注意:explicit关键字只能用于类内部的构造函数声明上,在类的定义体外部所作的定义上不应该再重复它:

//err.explicit allowed only on constructor declaration in class header

explicit ACEClass::ACEClass(int num)

{

         ...

}


这里补充说一点:explicit关键字用在具有多于一个参数的构造函数上不起作用的,因为当构造函数参数多于一个时,它默认不进行隐式类型转换。也就是说此时用不用explicit来限定构造函数都是一样的。当然,正如前面提过的,有一种例外情况,就是当调用构造函数时只传一个实参,其他参数有默认值时,此时explicit关键字仍将起它应有的作用。

MSDN中对explicit的叙述是这样的:explicit on a constructor with multiple arguments has no effect, since such constructors cannot take part in implicit conversions. However, for the purpose of implicit conversion, explicit will have an effect if a constructor has multiple arguments and all but one of the arguments has a default value.


最佳实践:通常,除非有明显的理由要定义隐式转换,否则,单形参构造函数应该声明为explicit。将构造函数设置为explicit可以避免错误,并且当转换有用时,用户可以显示的构造对象。