explicit 关键字

在 C++ 中,explicit 是一个用于构造函数的修饰符,其主要作用是 防止隐式类型转换,从而避免某些情况下的代码歧义或意外错误。


用法

  1. 修饰构造函数,防止隐式类型转换。
  2. 修饰带有单个参数的构造函数,避免单参数的构造函数被用作隐式类型转换运算符。

语法

1
2
3
4
class ClassName {
public:
explicit ClassName(Type param);
};

为什么需要 explicit

  • 默认情况下,C++ 会允许单参数构造函数被隐式调用,用于进行类型转换。这种行为有时会导致意外的错误。
  • 使用 explicit 可以禁止这样的隐式转换。

示例

1. 没有使用 explicit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>

class MyClass {
public:
// 单参数构造函数
MyClass(int value) : m_value(value) {}

void display() {
std::cout << "Value: " << m_value << std::endl;
}

private:
int m_value;
};

int main() {
MyClass obj = 42; // 隐式调用 MyClass(int) 构造函数
obj.display(); // 输出:Value: 42

return 0;
}

问题

  • MyClass obj = 42; 中,整数 42 被隐式转换为 MyClass 类型的对象。
  • 这种隐式转换可能导致意想不到的错误,尤其当构造函数的逻辑复杂或多义时。

2. 使用 explicit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>

class MyClass {
public:
explicit MyClass(int value) : m_value(value) {}

void display() {
std::cout << "Value: " << m_value << std::endl;
}

private:
int m_value;
};

int main() {
// MyClass obj = 42; // 错误:因为 explicit 禁止隐式转换
MyClass obj(42); // 必须显式调用构造函数
obj.display(); // 输出:Value: 42

return 0;
}

结果

  • 使用 explicit 后,MyClass obj = 42; 会编译报错。
  • 必须显式调用构造函数:MyClass obj(42);

更多例子

1. 防止误用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>

class Timer {
public:
explicit Timer(int seconds) : m_seconds(seconds) {}

void start() {
std::cout << "Timer started for " << m_seconds << " seconds." << std::endl;
}

private:
int m_seconds;
};

void setTimer(Timer t) {
t.start();
}

int main() {
// setTimer(10); // 错误:需要显式构造 Timer 对象
setTimer(Timer(10)); // 正确:显式构造
return 0;
}
  • 如果 Timer 构造函数未被 explicit 修饰,setTimer(10) 会隐式转换为 Timer(10),可能导致意外行为。

2. 避免容器中意外类型转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <vector>

class MyClass {
public:
explicit MyClass(int value) {}
};

int main() {
std::vector<MyClass> vec;

// vec.push_back(10); // 错误:explicit 禁止隐式转换
vec.push_back(MyClass(10)); // 正确:显式构造

return 0;
}

与非 explicit 的对比

特性 没有 explicit 使用 explicit
构造函数调用方式 允许隐式和显式调用 仅允许显式调用
隐式转换 支持隐式类型转换,可能导致意外错误 禁止隐式类型转换,减少歧义和错误风险
代码简洁性 可省略显式构造调用(Type obj = value; 必须显式调用构造函数(Type obj(value);

适用场景

  • 单参数构造函数:当类有单参数构造函数时,避免隐式转换带来的歧义。
  • 限制类型转换:需要明确控制何时允许类型转换。
  • 模板类:显式控制模板参数的构造,避免意外的模板匹配。

总结

  1. explicit 的作用

    • 防止隐式类型转换。
    • 提高代码的安全性和可读性,避免意外错误。
  2. 使用场景

    • 单参数构造函数。
    • 需要限制隐式转换时。
  3. 编程建议

    • 如果类的构造函数中有单参数,且你不希望它被隐式调用,**请使用 explicit**。