概述:
在平时我们定义一个类,然后创建类对象可以有多种方式,主要分为两种:
声明一个Student类:
class Student {
public:
Student(int age) {
m_age = age;
}private:
int m_age;
};
- 显示构建(explicit)
Student s1(5); // 像这种形式的构建对象,就是显示构建- 隐式构建(implicit)
Student s1 = 5; // 像这种形式的构建对象,就是隐式构建
如何区别显示构建和隐式构建?
在上述的隐式构建中,我们直接使用=将5赋值给了对象s1,这是这个表达式给我们的直观印象(也是这个原因,才提出explicit关键字)。但是,它并不是赋值,编译器会根据赋值变量的类型(对象),去调用相应的构造函数,=后面的数字会作为参数,来实现堆对象的构建。
而显示构建是使用()运算符传入一个参数,去构建对象,很直观的可以看出其是通过调用某个函数(构造函数),去创建对象。
- explicit关键字的作用
explicit的意思就是显示的意思,它是用来修饰类的构造函数的,被explicit修饰的构造函数,我们在调用它去构建对象的时候,只能显示的去构建,不能隐式的去构建。
- 使用explicit的原因
隐式构建对象Student s1 = 5; 这样的表达式直观看来就好像是直接将5赋值给s1,而其实际是去调用对应的构造函数,并不是一个赋值语句。
所以,为了避免这种歧义,我们使用explicit关键字,来指定使用对应构造函数去构造对象的时候,只能显示的去构造,不能使用上面隐式的方式,来避免这种歧义。
explicit作用于普通构造函数
下面代码中,我们使用explicit关键字修饰Student类的构造函数,会发现下面隐式的去构造s1对象的时候报错了。
explicit关键字作用于拷贝构造函数
和上面同理,我们也可以将explicit关键字作用于拷贝构造函数上,这样其也复发使用=进行构建了。
下面代码中会发现,我们使用explicit关键字修饰拷贝构造函数,Student s1 = Student(5); // 这样去调用靠北构造函数也就会报错。
使用explicit之后在函数传参的时候应该注意
没加explicit关键字
class Student {
public:
Student(int age) {
m_age = age;
}
Student(const Student& stu) {
this->m_age = stu.m_age;
}
private:
int m_age;
};
void Demo(Student s1) {
std::cout << "Demo" << std::endl;
}
int main(void) {
Student s1(5);
Demo(5);
Demo(s1);
std::cin.get();
return 0;
}
我们写了Demo函数,其形式参数我们传入一个Student类的对象,会发现main中我们给函数传递5,或者s1对象是允许的。
Demo(5); // 就相当于Student s1 = 5;Demo(s1); // 就相当于 Student s1 = s1;
加上explicit关键字
会发现加上explicit关键字之后,我们上面调用Demo()就出现了问题,因为上面说了,函数调用的过程其实就是隐式构建对象的过程,所以加上explicit之后就不能使用隐式构建对象了,自然就报错了。
总结:
explicit关键字就是为了解决隐式构建对象过程中带来的歧义,会容易让人理解为赋值语句,而不是调用构造函数。
给相应的构造函数加上explicit关键字之后,我们就不能在使用=去调用构造函数了(隐式)。(普通构造函数或者拷贝构造函数都是)
其实,拷贝构造函数即使使用=隐式构建,也并不会有太大歧义,当然也是根据自己的需求来定。
注意: c++中只有explicit关键字来指定显示构建对象,是没有implicit关键字的,我们只要什么也不写就默认是隐式的了,隐式的状态下既可以隐式构建对象,也可以显示构建对象。