日常分享整合的学习笔记:
首先,explict 中文意思是 清楚明白的;易于理解的;明确的;直言的 ;显式;
在编程中:
explicit用来告诉编译器:"不要偷偷用这个构造函数做隐式类型转换!
1.先看一个反面例子(没有 explicit)
假设有一个类:
cpp
class Number {
public:
Number(int x) { // 只有一个参数的构造函数
value = x;
}
private:
int value;
};
void printNumber(Number n) {
// 假装打印一下...
}
int main() {
printNumber(42); // 这里发生了甚么?
}
printNumber(42);
在这里你本来想传入一个Number类给 printNumber,但你传错了
而且这里发生了隐式类型转换,等价于:
cpp
Number n = 42; //这个是可以编译过的,可以试一下并输出一下value成员变量,结果是42
//相当于调用构造函数 Number n(42);
这很奇怪对吧?
因为编译器看到 printNumber需要 Number对象,但你传了一个 int,恰好是Number类对应的构造函数参数,于是它会自动调用 Number(int)构造函数把 42变成 Number(42)。这就是隐式类型转换。
加上 explicit 之后:
cpp
class Number {
public:
explicit Number(int x) { // 加了个 explicit
value = x;
}
private:
int value;
};
void printNumber(Number n) { /* ... */ }
int main() {
// printNumber(42); // 这行现在会报错!
printNumber(Number(42)); // 必须显式构造,这样写才行
}
加了 explicit后,编译器不会再偷偷帮你把 42转换成 Number,你必须自己明确写出构造过程。这让代码意图更清晰,也避免了一些隐蔽的错误。
2. 多参数构造函数(C++11 起)
从 C++11 开始,explicit也可以用在多参数构造函数上。它的作用是禁止使用花括号初始化列表进行隐式转换。
cpp
class Point {
public:
explicit Point(int x, int y) : mx(x), my(y) {} //初始化列表
private:
int mx, my;
};
void draw(Point p) {}
int main() {
draw({1, 2}); // ❌ 错误:不能隐式转换
draw(Point{1, 2}); // ✅ 正确:显式构造
}
注意 :这里说的是花括号初始化列表 {1, 2}这种写法。如果是 draw(Point(1, 2))圆括号构造,本来就没有隐式转换的问题。
3.常见疑惑与陷阱
疑惑 1:explicit 会影响拷贝/移动构造函数吗?
不影响。 explicit只影响普通构造函数 和转换运算符 。拷贝构造函数和移动构造函数即使加了 explicit,也不影响它们正常的拷贝/移动行为(但一般没人给它们加)。
cpp
class Foo {
public:
explicit Foo(int) {} // 普通构造,禁止隐式转换
// 拷贝构造函数不需要 explicit
Foo(const Foo&) = default;
};
疑惑 2:explicit 和 = default / = delete 一起用?
可以。比如你想禁止某些隐式转换,但仍然保留默认构造:
cpp
class Bar {
public:
explicit Bar(int) = default; // 显式构造且使用默认实现
Bar(double) = delete; // 完全禁止 double 类型的构造
};
疑惑 3:explicit 到底能不能提高性能?
通常不能。 explicit不影响运行时的性能,它只影响编译器的行为------是否允许某种写法通过编译。所以不用担心加了 explicit会让程序变慢
疑惑 4:什么时候应该不加 explicit?
很少见,但有几种情况可以考虑不加:
-
包装类 :比如一个类只是对底层类型的一层薄包装,而且经常需要和底层类型互操作。例如
std::string_view可以用const char*隐式构造。 -
数值类型 :比如你自己写的
BigInteger类,允许int隐式转换成它,可能会方便一些。 -
仿函数/适配器:某些设计模式里,隐式转换反而让代码更简洁。
不过作为新手,先一律加 explicit,等你真的遇到需要去掉的情况再改,这样最安全。
4.什么时候用?
建议:所有单参数构造函数都建议加上 explicit,除非你有非常明确的理由需要隐式转换(这种情况极少)。
比如标准库里的 std::vector有一个 explicit vector(size_t count),就是为了防止你不小心写出这样的代码:
cpp
std::vector<int> v = 10; // 如果没有 explicit,这行可能就编译过了,但语义很奇怪
End.............
如果这篇笔记对你有帮助,留下赞蟹蟹