C++中explicit关键字的作用

本文详细解释了C++中explicit关键字的作用及其应用场景。通过实例说明如何使用explicit关键字来阻止构造函数的隐式转换,并探讨了其在代码设计中的优缺点。

原文地址 http://www.cnblogs.com/winnersun/archive/2011/07/16/2108440.html

 

explicit用来防止由构造函数定义的隐式转换。

要明白它的作用,首先要了解隐式转换:可以用单个实参来调用的构造函数定义了从形参类型到该类类型的一个隐式转换。

例如:

复制代码
class things
{
    public:
        things(const std::string &name = ""):
              m_name(name),height(0),weight(10){}
        int CompareTo(const things & other);
        std::string m_name;
        int height;
        int weight;
};
复制代码

  

这里things的构造函数可以只用一个实参完成初始化。所以可以进行一个隐式转换,像下面这样:

things a;
................//在这里被初始化并使用。
std::string nm = "book_1";
//由于可以隐式转换,所以可以下面这样使用
int result = a.CompareTo(nm);

  这段程序使用一个string类型对象作为实参传给things的CompareTo函数。这个函数本来是需要一个tings对象作为实参。现在编译器使用string nm来构造并初始化一个

 things对象,新生成的临时的things对象被传递给CompareTo函数,并在离开这段函数后被析构。

  这种行为的正确与否取决于业务需要。假如你只是想测试一下a的重量与10的大小之比,这么做也许是方便的。但是假如在CompareTo函数中还涉及到了要除以初始化为0的height属性,那么这么做可能就是错误的。需要在构造tings之后更改height属性不为0。所以要限制这种隐式类型转换。

  那么这时候就可以通过将构造函数声明为explicit,来防止隐式类型转换。

  explicit关键字只能用于类内部的构造函数声明上,而不能用在类外部的函数定义上。现在things类像这样:

class things
{
    public:
        explicit things(const std::string &name = ""):
              m_name(name),height(0),weight(0){}
        int CompareTo(const things & other);
        std::string m_name;
        int height;
        int weight;
};

  这时再进行编译,在vs2008上会提示:没有可用于执行该转换的用户定义的转换运算符,或者无法调用该运算符。

  这时你仍然可以通过显示使用构造函数完成上面的类型转换:

things a;
................//在这里被初始化并使用。
std::string nm = "book_1";
//显示使用构造函数
int result = a.CompareTo(things(nm));

  google的c++规范中提到explicit的优点是可以避免不合时宜的类型变换,缺点无。所以google约定所有单参数的构造函数都必须是显示的,只有极少数情况下拷贝构造函数可以不声明称explicit。例如作为其他类的透明包装器的类。

  effective c++中说:被声明为explicit的构造函数通常比其non-explicit兄弟更受欢迎。因为它们禁止编译器执行非预期(往往也不被期望)的类型转换。除非我有一个好理由允许构造函数被用于隐式类型转换,否则我会把它声明为explicit。我鼓励你遵循相同的政策。

 

<think>我们正在讨论C++中的explicit关键字。该关键字用于修饰构造函数,防止编译器进行隐式类型转换,以避免意外的类型转换发生。 通常,当构造函数只有一个参数(或者有多个参数,但除了第一个参数外都有默认值)时,编译器可能会使用该构造函数进行隐式转换。 使用explicit关键字可以防止这种隐式转换,只能进行显式转换。 下面我们通过示例代码来说明。</think>### C++中`explicit`关键字作用详解 `explicit`是C++中用于修饰构造函数的特殊关键字,其主要作用是**禁止编译器执行隐式类型转换**,确保类型转换只能通过显式方式进行。 #### 核心作用: 1. **防止隐式类型转换**:避免编译器自动调用构造函数进行不期望的类型转换 2. **提高代码安全性**:消除可能导致意外行为的隐式转换 3. **增强代码可读性**:明确标识需要显式调用的构造函数 ### 代码示例分析 #### 示例1:隐式转换的问题 ```cpp class Distance { public: // 允许隐式转换的构造函数 Distance(double meters) : m_meters(meters) {} void show() const { std::cout << "Distance: " << m_meters << " meters\n"; } private: double m_meters; }; int main() { Distance d1 = 5.0; // 隐式转换:double -> Distance ✅ Distance d2 = 100; // 隐式转换:int -> Distance ✅ d1 = 3.14; // 隐式转换:double -> Distance ✅ d2 = 200; // 隐式转换:int -> Distance ✅ d1.show(); // 输出:Distance: 3.14 meters d2.show(); // 输出:Distance: 200 meters // 可能意外发生的情况 if (d1 == 10.0) { // 隐式转换:double -> Distance ❌ // 这个比较可能不是开发者本意 } return 0; } ``` #### 示例2:使用`explicit`解决隐式转换问题 ```cpp class SafeDistance { public: // 使用explicit禁止隐式转换 explicit SafeDistance(double meters) : m_meters(meters) {} void show() const { std::cout << "SafeDistance: " << m_meters << " meters\n"; } private: double m_meters; }; int main() { // SafeDistance sd1 = 5.0; // 错误:不能隐式转换 ❌ // SafeDistance sd2 = 100; // 错误:不能隐式转换 ❌ SafeDistance sd1(5.0); // 正确:显式构造 ✅ SafeDistance sd2(100); // 正确:显式构造 ✅ // sd1 = 3.14; // 错误:不能隐式赋值 ❌ sd1 = SafeDistance(3.14); // 正确:显式转换 ✅ sd2 = static_cast<SafeDistance>(200); // 正确:显式转换 ✅ sd1.show(); // 输出:SafeDistance: 3.14 meters sd2.show(); // 输出:SafeDistance: 200 meters // 避免意外比较 // if (sd1 == 10.0) { // 错误:不能隐式转换 ❌ // // 编译错误,防止意外行为 // } return 0; } ``` ### 关键应用场景 1. **单参数构造函数**: ```cpp class StringWrapper { public: explicit StringWrapper(const char* str) : data(str) {} private: std::string data; }; // StringWrapper s = "hello"; // 错误 StringWrapper s("hello"); // 正确 ``` 2. **多参数构造函数(C++11起)**: ```cpp class Point { public: explicit Point(int x, int y) : x(x), y(y) {} private: int x, y; }; // Point p = {1, 2}; // 错误 Point p(1, 2); // 正确 ``` 3. **转换运算符(C++11起)**: ```cpp class SmartBool { public: explicit operator bool() const { return value; } private: bool value; }; SmartBool sb; // if (sb) {...} // 错误 if (static_cast<bool>(sb)) {...} // 正确 ``` ### 何时使用`explicit` | 场景 | 是否推荐使用`explicit` | 原因 | |------|------------------------|------| | 单参数构造函数 | ✅ 强烈推荐 | 防止意外类型转换 | | 多参数构造函数 | ✅ 推荐 | 防止列表初始化意外转换 | | 转换函数 | ✅ 推荐 | 防止意外布尔转换 | | 复制构造函数 | ❌ 不推荐 | 会禁用复制初始化 | | 移动构造函数 | ❌ 不推荐 | 会禁用移动初始化 | ### 最佳实践建议 1. **默认使用`explicit`**: ```cpp // 好的实践:默认添加explicit class SafeClass { public: explicit SafeClass(int value); }; ``` 2. **仅在需要隐式转换时省略**: ```cpp // 特殊场景:设计需要隐式转换 class ImplicitConverter { public: ImplicitConverter(int value); // 允许隐式转换 }; ``` 3. **结合`delete`禁用特定转换**: ```cpp class SpecialNumber { public: explicit SpecialNumber(int value); SpecialNumber(double) = delete; // 禁用double转换 }; ``` ### 总结 `explicit`关键字C++类型安全的重要工具: 1. 防止不期望的隐式类型转换 2. 使代码意图更明确 3. 减少运行时错误 4. 提高代码可维护性
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值