explicit意为“显式的”,该关键字主要是用于防止类构造函数出现隐式类型转换的情况,且只适用于仅含一个参数的构造函数。
我们先来看第一个问题:什么是防止构造函数出现隐式转换呢?来看下面的例子:
class A
{
public :
A(int a)
{
cout << "Constructor called and param is "<<a<<" ! " << endl;
m = a;
}
A()
{
cout << "Default Constructor called !" << endl;
};
~A()
{
cout << "Destructor called ! " << endl;
}
int m=3;
};
int _tmain(int argc, _TCHAR* argv[])
{
A a;
a=10;
cout << "m = "<<a.m << endl;
system("pause");
return 0;
}
a=10;一句中,a为类A的一个对象,将10赋值给一个对象,这显然是不应该的,但是在这种情况下编译器并不会报错,我们先来这段代码的运行结果:
可以看到,这里调用了两次构造函数,以及一次析构函数。其中第一个默认构造函数是构造对象a时调用的,那么第二次调用的含参构造函数是什么时候调用的呢?还有一点值得注意的是,这里析构函数的调用是在cout<<a.m一句前,并且这里的析构函数肯定不是对象a的,那么这多出来一次构造函数和一次析构函数是怎么来的呢?
实际上,这里的a=10;一句等价于A temp(10); a=temp; 即先构造一个临时对象A temp(10);然后再将temp拷贝给a后立刻将temp析构,因此,多出来的一次构造函数以及析构函数实际上是这个临时对象temp的,这个过程也就相当于将常量10隐式转换成了一个对象。因此,一旦出现了这种情况,编译器并不会报错,由此也可能造成最终意想不到的结果以及构造临时对象时效率的降低。
为了避免以上情况的发生,除了开发人员自行注意以外,还可以借助explicit关键字来防止隐式转换的发生。只需要在构造函数前加上该关键字,即explicit A(int a){...},那么编译器就会对上述情况报错,如下所示:
再来看第二个问题,为什么explicit只适用于仅含一个参数的构造函数?
这是因为除了仅含一个参数的构造函数以外,其他类型的构造函数也不可能出现上述情况了,因为你不可能将0个或2个、3个值赋值给一个对象。