一、前言
在编写 C++ 代码时,explicit 关键字出现的频率很高。理解其作用对于掌握类型转换机制至关重要。
二、explicit 关键字是什么?
explicit 是 C++ 中的关键字,用于修饰只有一个参数的类构造函数,表明该构造函数是显式的,而非隐式的。当使用 explicit 修饰构造函数时,它将禁止类对象之间的隐式转换,以及禁止隐式调用拷贝构造函数。
三、构造函数还具有类型转换的作用
单参构造函数
通过日期类进行讲解:
class Date {
public:
// 构造函数
Date(int year) : _year(year) {}
private:
int _year;
int _month = 3;
int _day = 31;
};
对于下面的代码,d1 和 d2 都会调用构造函数:
int main() {
Date d1(2022);
Date d2 = 2023;
return 0;
}
Date d2 = 2023 这种写法也会调用构造函数。这是因为存在隐式类型转换。将 int 类型的数值赋值给 double 类型的数据时,会产生隐式类型转换,中间会构造一个临时变量。
同理,2023 会先去构造一个临时对象(类型为 Date),将其 year 初始化为 2023,然后再通过这个临时对象进行拷贝构造给到 d2。编译器通常会优化这一过程,将【构造 + 拷贝构造】优化为【一个构造】。
若尝试将内置类型直接引用赋值给类类型对象:
Date& d3 = 2024;
这会报错,因为 d3 是 Date 类型,2024 是内置类型。但如果加 const 修饰:
const Date& d3 = { 2024, 4 };
则不会出现问题,因为产生的临时变量具有常性,可以与 const 引用绑定。
引出 explicit 关键字
如果不想让这种隐式类型转换发生,可以使用 explicit 关键字。它加在构造函数的前面进行修饰,有了它就不会发生上面的隐式转换。
explicit Date(int year) : _year(year) {}
多参构造函数
对于多参构造函数:
Date(int year, int month, int day = 31) : _year(year), _month(month), _day(day) {}
根据从右往左缺省的规则,初始化时需要传入两个参数。若像单参那样初始化,编译器会报错。
对于一定要传入多参数进行构造的场景,可以使用 C++11 的新特性,在外面加上一个 {} 来调用多参构造函数:
Date d2 = { 2023, 3 };
不仅如此,对于下面这种也同样适用,调用构造去产生一个临时对象:
const Date& d3 = { 2024, 4 };
那要如何去防止这样的隐式类型转换发生呢?还是可以使用到 explicit 关键字吗?
// 多参构造函数
explicit Date(int year, int month, int day = 31) : _year(year), _month(month), _day(day) {}
可以看到,加上 explicit 关键字做修饰,同样可以起到禁止类型转换的作用。
还有一种例外,当缺省参数从右往左给到两个的时候,此时只需要传入一个实参即可,那也就相当于是单参构造,explicit 关键字依旧可以起到作用。
explicit Date(int year, int month = 3, int day = 31) : _year(year), _month(month), _day(day) {}
为什么需要 explicit 关键字?
对于可读性不是很好的代码,可以使用 explicit 修饰构造函数,将会禁止构造函数的隐式转换,避免潜在的类型混淆错误。
怎么使用 explicit 关键字?
explicit 关键字在 C++ 中只能用于修饰类的构造函数,它用来修饰只有一个参数的类构造函数,以表明该构造函数是显式的,而非隐式的。只要在构造函数名前加上 explicit 就能禁止类对象之间的隐式转换、禁止隐式调用拷贝构造函数。
四、总结
explicit 关键字主要用于控制构造函数的隐式转换行为,提升代码的安全性和可读性。在使用单参构造函数时,建议默认添加 explicit 除非确实需要隐式转换。

