C++构造函数中explicit详解

2025-12-03 18:50

本文主要是介绍C++构造函数中explicit详解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《C++构造函数中explicit详解》explicit关键字用于修饰单参数构造函数或可以看作单参数的构造函数,阻止编译器进行隐式类型转换或拷贝初始化,本文就来介绍explicit的使用,感兴趣的可以...

在 C++ 编程中,构造函数是类的核心部分之一。我们常常使用构造函数来初始化对象。但是,如果不加限制,某些构造函数可能会被 隐式调用,从而带来一些意料之外的行为。

为了解决这个问题,C++ 提供了 explicit 关键字。

1. 什么是explicit

在 C++ 中,explicit 关键字用于修饰 单参数构造函数可以看作单参数的构造函数,阻止编译器进行 隐式类型转换拷贝初始化

  • 默认情况下,单参数构造函数既可以显式调用,也可以被编译器用来进行隐式类型转换。
  • explicit 告诉编译器:这个构造函数只能显式调用,不能用于隐式转换。

2. 隐式转换的问题

来看一个例子:

#include <IOStream>
using namespace std;

class Fraction {
private:
    int numerator;
    int denominator;

public:
    Fraction(int num, int den = 1) : numerator(num), denominator(den) {}
    void print() const {
        cout << numerator << "/" << denominator << endl;
    }
};

int main() {
    Fraction f1 = 5;  // 隐式调用 Fraction(5, 1)
    f1.print();       // 输出:5/1
}

在上面的例子中:

  • Fraction f1 = 5; 本质上是调用 China编程Fraction(5, 1),因为编译器允许用 int 隐式转换成 Fraction
  • 虽然看似方便,但有时会带来 不可控的隐式转换,导致逻辑错误或二义性。

3.explicit的使用示例

基本用法

如果我们在构造函数前加上 explicit

class Fraction {
private:
    int numerator;
    int denominator;

public:
    explicit Fraction(int num, int den = 1) : numerator(num), denominator(den) {}
    vo编程id print() const {
        cout << numerator << "/" << denominator << endl;
    }
};

int main() {
    Fraction f1(5);     // ✅ 显式调用,可以
    // Fraction f2 = 5; // ❌ 编译错误,不能隐式转换
}
  • Fraction f1(5); 依然可以显式调用。
  • Fraction f2 = 5; 会报错,因为 explicit 禁止了隐式转换。

多参数构造函数

有时构造函数有多个参数,但如果除第一个外的参数都有默认值,它依然算作 单参数构造函数,也可能引发隐式转换。

class Fraction {
public:
    explicit Fraction(int num, int den = 1) { /* ... */ }
};

这里如果没有 explicit,表达式 Fraction f = 5; 依然会成立。

4. C++11 之后的扩展

(1)explicit用于转换运算符

在 C++11 之前,类的类型转换函数(比如 operator bool)会允许隐式转换:

class Test {
public:
    operator bool() const { return true; }
};

int main() {
    Test t;
    if (t) {  // 隐式调用 operator bool()
        cout << "True" << endl;
    }
}

但有时我们并不希望这种隐式转换。C++javascript11 允许写成:

class Test {
public:
    explicit operator bool() const { return true; }
};

int main() {
    Test t;
    // if (t) { } // ❌ 错误,不能隐式转换
    if (static_cast<bool>(t)) {  // ✅ 必须显式转换
        cout << "True" << endl;
    }
}

(2) C++20 的explicihttp://www.chinasem.cnt(bool)

C++20 引入了更灵活的语法:explicit(bool)
这让我们可以根据编译期常量决定是否允许隐式调用。

struct A {
    androidexplicit(true) A(int) {}   // 永远显式
    explicit(false) A(double) {} // 永远允许隐式
};

这种写法在模板编程中很有用。

5. 最佳实践

  1. 几乎总是给单参数构造函数加 explicit
    这样可以避免隐式转换带来的混乱,除非你确实需要这种转换。

  2. 转换运算符应当尽量显式
    尤其是 operator bool,因为隐式转换到 bool 可能导致奇怪的条件判断。

  3. 允许隐式转换的场景
    如果你的类本质上就是包装某个类型(比如 string_view 可以从 const char* 隐式转换),那么允许隐式转换可以让使用更加自然。

总结

  • explicit 的主要作用:防止构造函数或转换运算符被隐式调用。
  • 在单参数构造函数和转换运算符中使用最为常见。
  • 自 C++11 起,还能用于 operator bool;C++20 引入 explicit(bool),进一步增强灵活性。
  • 最佳实践:默认加上 explicit,除非你有充分理由允许隐式转换。
  • 关键字explicit只对一个实参的构造函数有效,需要多个实参的构造函数不能用于执行隐式转换,所以无需将这些构造函数指定为explicit的。
  • 只能在类内声明构造函数时使用explicit关键字。

补充

  • 接受一个单参数的const char*的string构造函数不是explicit的
  • 接受一个容量参数的vector构造函数是explicit的

到此这篇关于C++构造函数中explicit详解的文章就介绍到这了,更多相关C++构造函数explicit内容请搜索China编程(www.chinasem.cn)以前的文章或继续浏览下面的相关文章希望大家以后多多支持China编程(www.chinasem.cn)!

这篇关于C++构造函数中explicit详解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1156344

相关文章

C++ move 的作用详解及陷阱最佳实践

《C++move的作用详解及陷阱最佳实践》文章详细介绍了C++中的`std::move`函数的作用,包括为什么需要它、它的本质、典型使用场景、以及一些常见陷阱和最佳实践,感兴趣的朋友跟随小编一起看... 目录C++ move 的作用详解一、一句话总结二、为什么需要 move?C++98/03 的痛点⚡C++

MySQL中between and的基本用法、范围查询示例详解

《MySQL中betweenand的基本用法、范围查询示例详解》BETWEENAND操作符在MySQL中用于选择在两个值之间的数据,包括边界值,它支持数值和日期类型,示例展示了如何使用BETWEEN... 目录一、between and语法二、使用示例2.1、betwphpeen and数值查询2.2、be

python中的flask_sqlalchemy的使用及示例详解

《python中的flask_sqlalchemy的使用及示例详解》文章主要介绍了在使用SQLAlchemy创建模型实例时,通过元类动态创建实例的方式,并说明了如何在实例化时执行__init__方法,... 目录@orm.reconstructorSQLAlchemy的回滚关联其他模型数据库基本操作将数据添

Java中ArrayList与顺序表示例详解

《Java中ArrayList与顺序表示例详解》顺序表是在计算机内存中以数组的形式保存的线性表,是指用一组地址连续的存储单元依次存储数据元素的线性结构,:本文主要介绍Java中ArrayList与... 目录前言一、Java集合框架核心接口与分类ArrayList二、顺序表数据结构中的顺序表三、常用代码手动

JAVA线程的周期及调度机制详解

《JAVA线程的周期及调度机制详解》Java线程的生命周期包括NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING和TERMINATED,线程调度依赖操作系统,采用抢占... 目录Java线程的生命周期线程状态转换示例代码JAVA线程调度机制优先级设置示例注意事项JAVA线程

详解C++ 存储二进制数据容器的几种方法

《详解C++存储二进制数据容器的几种方法》本文主要介绍了详解C++存储二进制数据容器,包括std::vector、std::array、std::string、std::bitset和std::ve... 目录1.std::vector<uint8_t>(最常用)特点:适用场景:示例:2.std::arra

Android使用java实现网络连通性检查详解

《Android使用java实现网络连通性检查详解》这篇文章主要为大家详细介绍了Android使用java实现网络连通性检查的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录NetCheck.Java(可直接拷贝)使用示例(Activity/Fragment 内)权限要求

C++,C#,Rust,Go,Java,Python,JavaScript的性能对比全面讲解

《C++,C#,Rust,Go,Java,Python,JavaScript的性能对比全面讲解》:本文主要介绍C++,C#,Rust,Go,Java,Python,JavaScript性能对比全面... 目录编程语言性能对比、核心优势与最佳使用场景性能对比表格C++C#RustGoJavapythonjav

MyBatis中的两种参数传递类型详解(示例代码)

《MyBatis中的两种参数传递类型详解(示例代码)》文章介绍了MyBatis中传递多个参数的两种方式,使用Map和使用@Param注解或封装POJO,Map方式适用于动态、不固定的参数,但可读性和安... 目录✅ android方式一:使用Map<String, Object>✅ 方式二:使用@Param

C++打印 vector的几种方法小结

《C++打印vector的几种方法小结》本文介绍了C++中遍历vector的几种方法,包括使用迭代器、auto关键字、typedef、计数器以及C++11引入的范围基础循环,具有一定的参考价值,感兴... 目录1. 使用迭代器2. 使用 auto (C++11) / typedef / type alias