C++中支持explicit(直译过来表示明白的,不隐晦的)关键字,要详细叙述该关键字要从复制构造器——类型转换构造器说起。
C++编写类时,有一种特殊的构造器——复制构造器,它只有一个参数,参数类型是本类实例的引用(常引用也可,如果是常引用可以使得该构造的应用范围更广),复制构造器如果类的设计者没有编写编译器就会自动生成。默认的复制构造器的作用多数情况下是实现从参数对象到创建对象的逐个字节的复制,即使得创建的对象的每个成员变量都与参数对象相等。(如果自己编写复制构造器最好不要改变其默认的作用)。
另外一种特殊的构造器是类型转换构造器,只有一个参数,且该参数不是本类实例的引用或常引用的构造器称为类型转换构造器。
以下是示例:
#include <iostream>
using namespace std;
class Complex
{
public:
double real,imag;
//普通构造器
Complex(double _real,double _imag):real(_real),imag(_imag){}
//复制构造器
implicit Complex(const Complex& c):real(c.real),imag(c.imag){
cout<<"COPY CONSTRUCTOR IS CALLED"<<endl;
}
//类型转换构造器
Complex(int a):real(a){}
};
int main()
{
//调用普通构造器创建实例c1;
Complex c1(2,2);
cout<<c1.real<<" "<<c1.imag<<endl;
//调用复制构造器创建实例c2;
Complex c2(c1);
cout<<c2.real<<" "<<c1.imag<<endl;
//调用类型装换构造器;
Complex c3 = 9;
cout<<c3.real<<" "<<c3.imag<<endl
}
以下为以上代码输出:
2 2
COPY CONSTRUCTOR IS CALLED
2 2
CONVERT CONSTRUCTOR IS CALLED
9 0
代码中,在初始化c3时,初始化符号两边是不相等的,之所以不会报错,是因为在初始化右边创建了一个临时的Complx实例,该临时对象以9为实参,调用类型转换构造器初始化,然后将用临时对象初始化c3。
但是有的时候,由于需要提高代码的易读性原因,我们是不希望这种隐式转换发生的,此时,我们就可以用explicit关键字对类型转换构造器进行修饰,来禁用这种隐式类型转换。
#include <iostream>
using namespace std;
class Complex
{
public:
double real,imag;
//普通构造器
Complex(double _real,double _imag):real(_real),imag(_imag){}
//复制构造器
Complex(const Complex& c):real(c.real),imag(c.imag){
cout<<"COPY CONSTRUCTOR IS CALLED"<<endl;
}
//类型转换构造器
explicit Complex(int a):real(a){
cout<<"CONVERT CONSTRUCTOR IS CALLED"<<endl;
}
};
int main()
{
//调用普通构造器创建实例c1;
Complex c1(2,2);
cout<<c1.real<<" "<<c1.imag<<endl;
//调用复制构造器创建实例c2;
Complex c2(c1);
cout<<c2.real<<" "<<c1.imag<<endl;
//调用类型装换构造器;
//以下语句报错,由于禁用通过类型转换构造器完成对int到Complex的类型转换
//Complex c3 = 9;
//cout<<c3.real<<" "<<c3.imag<<endl;
}
explicit只能修饰类型转换构造器吗?并不,该关键字同样可以修饰复制构造器,当explicit修饰类型转换构造器时并非禁用复制构造,而是禁止任何类型的隐式向当前类型转换。
#include <iostream>
using namespace std;
class Complex
{
public:
double real,imag;
//普通构造器
Complex(double _real,double _imag):real(_real),imag(_imag){}
//复制构造器
Complex(const Complex& c):real(c.real),imag(c.imag){
cout<<"COPY CONSTRUCTOR IS CALLED"<<endl;
}
//类型转换构造器
explicit Complex(int a):real(a){
cout<<"CONVERT CONSTRUCTOR IS CALLED"<<endl;
}
Complex(double b):real(b){
cout<<"CONVERT CONSTRUCTOR IS CALLED"<<endl;
}
};
int main()
{
//调用普通构造器创建实例c1;
Complex c1(2,2);
cout<<c1.real<<" "<<c1.imag<<endl;
//调用复制构造器创建实例c2;
Complex c2(c1);
cout<<c2.real<<" "<<c1.imag<<endl;
//调用类型装换构造器;
Complex c3 = 9;
cout<<c3.real<<" "<<c3.imag<<endl;
}
以上代码输出:
2 2
COPY CONSTRUCTOR IS CALLED
2 2
CONVERT CONSTRUCTOR IS CALLED
9 0
说明尽管我们禁用了int到Complex的隐式转换,但是,Complex c3=9;这条语句会将9转化为double继续调用double到Complex进行隐式转换。但是我们将explicit用于修饰复制构造器:
#include <iostream>
using namespace std;
class Complex
{
public:
double real,imag;
//普通构造器
Complex(double _real,double _imag):real(_real),imag(_imag){}
//复制构造器
explicit Complex(const Complex& c):real(c.real),imag(c.imag){
cout<<"COPY CONSTRUCTOR IS CALLED"<<endl;
}
//类型转换构造器
Complex(int a):real(a){
cout<<"CONVERT CONSTRUCTOR IS CALLED"<<endl;
}
Complex(double b):real(b){
cout<<"CONVERT CONSTRUCTOR IS CALLED"<<endl;
}
};
int main()
{
//调用普通构造器创建实例c1;
Complex c1(2,2);
cout<<c1.real<<" "<<c1.imag<<endl;
//调用复制构造器创建实例c2;
//复制构造器本身不会受到影响。
Complex c2(c1);
cout<<c2.real<<" "<<c1.imag<<endl;
//以下语句都会报错;即,既无法通过int到Complex的隐式转换;
//也无法通过double到Complex的隐式转换;
//Complex c3 = 9;
//cout<<c3.real<<" "<<c3.imag<<endl;
//Complex c4 = 1.2;
//cout<<c4.real<<" "<<c4.imag<<endl;
}
另外注意到,explicit通常作用于只有一个参数的构造器才有效果,但是这只是表象,如果一个构造器有多个参数,但是除了第一个参数全是有默认值的参数,那么这个构造器仍然是复制构造器或类型转换构造器,仍然可以使用explicit关键字来禁用隐式类型转换。