c++学习笔记分享--explicit关键字

日常分享整合的学习笔记:

首先,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?

很少见,但有几种情况可以考虑不加:

  1. 包装类 :比如一个类只是对底层类型的一层薄包装,而且经常需要和底层类型互操作。例如 std::string_view可以用 const char*隐式构造。

  2. 数值类型 :比如你自己写的 BigInteger类,允许 int隐式转换成它,可能会方便一些。

  3. 仿函数/适配器:某些设计模式里,隐式转换反而让代码更简洁。

不过作为新手,先一律加 explicit,等你真的遇到需要去掉的情况再改,这样最安全。

4.什么时候用?

建议:所有单参数构造函数都建议加上 explicit,除非你有非常明确的理由需要隐式转换(这种情况极少)。

比如标准库里的 std::vector有一个 explicit vector(size_t count),就是为了防止你不小心写出这样的代码:

cpp 复制代码
std::vector<int> v = 10;  // 如果没有 explicit,这行可能就编译过了,但语义很奇怪

End.............

如果这篇笔记对你有帮助,留下赞蟹蟹