浅析 explicit 关键字

浅析 explicit 关键字

文章目录

  • 浅析 explicit 关键字
    • 前言
    • 案例剖析
    • 补充案例
    • 总结

前言

​ C++ 提供了多种方式来实现类型转换和构造对象,然而,有时候这些方式会导致一些意想不到的结果,比如隐式转换复制初始化。为了避免这些潜在的问题,C++ 引入了 explicit 关键字,它可以限制一些不必要或不安全的转换和初始化。在本文中,我们将介绍 explicit 关键字的作用和用法,以及它如何提高代码的可读性和安全性。


案例剖析

​ 为了说明 explicit 关键字的作用和用法,我们首先定义两个结构体 A 和 B,它们都有一个接受 int 参数的构造函数和一个返回 bool 值的转换函数,但是 B 的构造函数和转换函数都被 explicit 修饰了,而 A 的没有。

struct A 
{
	A(int) {}
	operator bool() const { return true; }
};

struct B 
{
	explicit B(int) {}
	explicit operator bool() const { return true; }
};

接下来,我们定义两个函数 doA 和 doB,它们分别接受 A 和 B 类型的参数。

void doA(A a) {}

void doB(B b) {}

然后,我们在 main 函数中创建一些 A 和 B 类型的对象,并尝试用不同的方式进行初始化和转换。

int main() {
  A a1(1);     // OK:直接初始化
  A a2 = 1;    // OK:复制初始化
  A a3{1};     // OK:直接列表初始化
  A a4 = {1};  // OK:复制列表初始化
  A a5 = (A)1; // OK:允许 static_cast 的显式转换
  doA(1);      // OK:允许从 int 到 A 的隐式转换
  if (a1)
    ; // OK:使用转换函数 A::operator bool() 的从 A 到 bool 的隐式转换
  bool a6(a1); // OK:使用转换函数 A::operator bool() 的从 A 到 bool 的隐式转换
  bool a7 = a1; // OK:使用转换函数 A::operator bool() 的从 A 到 bool 的隐式转换
  bool a8 = static_cast<bool>(a1); // OK :static_cast 进行直接初始化

  B b1(1);     // OK:直接初始化
               //    B b2 = 1;        // 错误:被 explicit
               //    修饰构造函数的对象不可以复制初始化
  B b3{1};     // OK:直接列表初始化
               //    B b4 = { 1 };        // 错误:被 explicit
               //    修饰构造函数的对象不可以复制列表初始化
  B b5 = (B)1; // OK:允许 static_cast 的显式转换
               //    doB(1);            // 错误:被 explicit
               //    修饰构造函数的对象不可以从 int 到 B 的隐式转换
  if (b1)
    ; // OK:被 explicit 修饰转换函数 B::operator bool() 的对象可以从 B 到 bool
      // 的按语境转换
  bool b6(b1); // OK:被 explicit 修饰转换函数 B::operator bool() 的对象可以从 B
               // 到 bool 的按语境转换
               //    bool b7 = b1;        // 错误:被 explicit 修饰转换函数
               //    B::operator bool() 的对象不可以隐式转换
  bool b8 = static_cast<bool>(b1); // OK:static_cast 进行直接初始化

  return 0;
}

从上面的代码中,我们可以看出以下几点:

  • explicit 修饰的构造函数只能用于直接初始化直接列表初始化不能用于复制初始化和复制列表初始化这样可以避免一些不必要的类型转换,比如将一个 int 值赋给一个 B 类型的变量,或者将一个花括号列表赋给一个 B 类型的变量。
  • explicit 修饰的转换函数只能用于显式转换和按语境转换,不能用于隐式转换。这样可以避免一些不安全的类型转换,比如将一个 B 类型的对象赋给一个 bool 类型的变量,或者将一个 B 类型的对象作为条件表达式。按语境转换是指在某些特定的语境中,编译器会自动调用 explicit 修饰的转换函数,比如 if 语句,while 语句,for 语句,switch 语句,逻辑运算符,条件运算符,static_cast,和 bool 构造函数。
  • explicit 关键字可以提高代码的清晰度和一致性,避免一些潜在的错误和歧义。

补充案例

上面案例中A、B构造函数都属于单参数类型,下面印证仅有多参数构造函数的类是否存在同样的结论。此处以“分数”为例构建结构体案例:

例如,我们可以定义一个表示分数的结构体 Fraction,它有一个接受两个 int 参数的构造函数,表示分子和分母,以及一个返回 double 值的转换函数,表示分数的小数值。

我们可以用 explicit 关键字来修饰这两个函数,以防止一些不合理或不明确的转换和初始化

struct Fraction 
{
	int num; // 分子
	int den; // 分母

	// explicit 修饰的构造函数,防止从 int 或 int,int 到 Fraction 的隐式转换和复制初始化
	explicit Fraction(int n, int d = 1) : num(n), den(d) {}

	// explicit 修饰的转换函数,防止从 Fraction 到 double 的隐式转换
	explicit operator double() const { return static_cast<double>(num) / den; }
};

接着创建一些 Fraction 类型的对象,并尝试用不同的方式进行初始化和转换。

Fraction f1(1, 2); // OK:直接初始化
//		Fraction f2 = 1;        // 错误:被 explicit 修饰的构造函数不可以复制初始化
//		Fraction f3 = 1, 2;     // 错误:被 explicit 修饰的构造函数不可以复制初始化
Fraction f4{ 1, 2 }; // OK:直接列表初始化
//		Fraction f5 = { 1, 2 };	// 错误:被 explicit 修饰的构造函数不可以复制列表初始化
Fraction f6 = (Fraction)1;	// OK:允许 static_cast 的显式转换
Fraction f7 = static_cast<Fraction>(1); // OK:static_cast 进行直接初始化

//		double d1 = f1; // 错误:被 explicit 修饰的转换函数不可以隐式转换
double d2(f1);  // OK:被 explicit 修饰的转换函数可以按语境转换
double d3 = static_cast<double>(f1); // OK:static_cast 进行直接初始化

我们看到上面 f6、f7 对象通过类型强转生成 Fraction 对象这个过程属于显式转换,所以经过强转生成的临时对象可以成功执行拷贝构造和赋值运算函数将值传给 f6、f7 对象。

  • explicit 修饰的构造函数可以防止从 int 或 int,int 到 Fraction 的隐式转换和复制初始化。例如,如果我们想要创建一个表示 1/2 的分数,我们应该使用 Fraction f(1, 2) 或 Fraction f{1, 2},而不是 Fraction f = 1 或 Fraction f = {1, 2},因为后者会创建一个表示 1/1 或 2/1 的分数,这显然不是我们的目的。
  • explicit 修饰的转换函数可以防止从 Fraction 到 double 的隐式转换。例如,如果我们想要计算一个分数的倒数,我们应该使用 static_cast<double>(f)来显式地将分数转换为 double 类型,然后再进行除法运算,而不是直接使用 1 / f,因为后者会先将 1 隐式地转换为 Fraction 类型,然后再调用 Fraction 类的重载的除法运算符,这可能会得到一个错误的结果。

总结

  • explicit 修饰构造函数时,可以防止隐式转换和复制初始化
  • explicit 修饰转换函数时,可以防止隐式转换,但按语境转换除外

​ 在本文中介绍了 explicit 关键字的作用和用法,以及它如何提高代码的可读性和安全性。我们了解了 explicit 关键字可以用于修饰构造函数和转换函数,以阻止隐式转换和复制初始化,也了解了 explicit 修饰的构造函数和转换函数的使用场景和限制。我们认识到 explicit 关键字可以帮助我们避免一些不必要或不安全的类型转换,提高代码的清晰度和一致性。希望本文对你有所帮助,如果你有任何问题或建议,欢迎在评论区留言。谢谢!

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/423943.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Redis安全加固策略:配置文件权限设置 配置本地日志存储目录 连接超时时间限制

Redis安全加固策略&#xff1a;配置文件权限设置 & 配置本地日志存储目录 & 连接超时时间限制 1.1 配置文件权限设置1.2 配置本地日志存储目录1.3 连接超时时间限制 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 1.1 配置文件权限…

【双指针】合并两个有序数组O(N)

合并两个有序数组 链接 . - 力扣&#xff08;LeetCode&#xff09;. - 备战技术面试&#xff1f;力扣提供海量技术面试资源&#xff0c;帮助你高效提升编程技能,轻松拿下世界 IT 名企 Dream Offer。https://leetcode.cn/problems/merge-sorted-array/ 题目 题解 采用双指针…

Java项目:31 基于SSM的勤工俭学管理系统

作者主页&#xff1a;源码空间codegym 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文中获取源码 项目介绍 勤工助学系统有管理员&#xff0c;部门管理员&#xff0c;用户三个角色。 管理员功能有个人中心。管理员管理&#xff0c;部门管理员管理&…

vs code更新后json文件无法识别通配符 ,编译多文件失败的解决办法

问题描述 在Mac或者LInux上&#xff0c;进行C/C相同路径下进行多文件编译时&#xff0c;之前设置好的json文件突然不能解释通配符&#xff0c;并且将带有单引号的地址传给clang&#xff0c;由于*.c被扩在单引号中&#xff0c;clang找不到文件导致失败。 如果将命令端中的指令复…

新一代电话机器人开源PHP源代码

使用easyswoole 框架开发的 新一代电话机器人开源PHP源码 项目地址&#xff1a;https://gitee.com/ddrjcode/robotphp 代理商页面演示地址 http://119.23.229.15:8080 用户名&#xff1a;c0508 密码&#xff1a;123456 包含 AI外呼管理&#xff0c;话术管理&#xff0c;CR…

Android java基础_异常

一.异常的概念 在Java中&#xff0c;异常&#xff08;Exception&#xff09;是指程序执行过程中可能出现的不正常情况或错误。它是一个事件&#xff0c;它会干扰程序的正常执行流程&#xff0c;并可能导致程序出现错误或崩溃。 异常在Java中是以对象的形式表示的&#xff0c;…

力扣hot100题解(python版48-50题)

48、路径总和III 给定一个二叉树的根节点 root &#xff0c;和一个整数 targetSum &#xff0c;求该二叉树里节点值之和等于 targetSum 的 路径 的数目。 路径 不需要从根节点开始&#xff0c;也不需要在叶子节点结束&#xff0c;但是路径方向必须是向下的&#xff08;只能从…

香港服务器选择指南(挑选香港服务器的几个标准)

​  随着全球化的加速和互联网的普及&#xff0c;跨境访问和外贸活动越来越频繁。在这个背景下&#xff0c;香港服务器作为一种国际化的基础设施&#xff0c;受到了广泛欢迎。本文将探讨企业在选择香港服务器时应关注的几个标准事项。 1.可靠性和正常运行时间 停机可能会给企…

Linux:kubernetes(k8s)node节点加入master主节点(3)

Linux&#xff1a;kubernetes&#xff08;k8s&#xff09;搭建mater节点&#xff08;kubeadm&#xff0c;kubectl&#xff0c;kubelet&#xff09;-CSDN博客https://blog.csdn.net/w14768855/article/details/136415575?spm1001.2014.3001.5502 我在上一章部署好了主节点&…

qsort使用

qsort 是用来排序的数据的库函数,底层使用的是快速排序的方式 排序方式有:选择,冒泡,插入,快速, 希尔...... 对于qsort这个库函数: void qsort(void* base,size_t num,size_t size,int (*compar)(const void*,const void*) 其中 void* base 是指针,指向的是待排序的数组的第…

棋牌室计时计费管理系统的灯控器连接教程

棋牌室计时计费管理系统的灯控器连接教程 一、前言 以下教程以 佳易王棋牌室计时计费管理系统软件V18.0为例说明 软件文件下载可以点击最下方官网卡片——软件下载——试用版软件下载 如上图&#xff0c;计时计费软件在开始计时的时候&#xff0c;点击 开始计时 如果连接了…

第19章-IPv6基础

1. IPv4的缺陷 2. IPv6的优势 3. 地址格式 3.1 格式 3.2 长度 4. 地址书写压缩 4.1 段内前导0压缩 4.2 全0段压缩 4.3 例子1 4.4 例子 5. 网段划分 5.1 前缀 5.2 接口标识符 5.3 前缀长度 5.4 地址规模分类 6. 地址分类 6.1 单播地址 6.2 组播地址 6.3 任播地址 6.4 例子 …

java-ssm-jsp-宠物常规护理知识管理系统设计与实现

java-ssm-jsp-宠物常规护理知识管理系统设计与实现 获取源码——》公主号&#xff1a;计算机专业毕设大全

VirtualBox 桥接网卡 未指定 “未能启动虚拟电脑Ubuntu,由于下述物理网卡未找到:”

解决办法&#xff0c;安装虚拟网卡&#xff0c;win11查找方式&#xff1a;控制面板→网络和共享中心→更改适配器设置 此时出现下面情况就算安装成功 但是如果报错&#xff1a;找不到指定的模块 则按下面步骤删除干净垃圾重新上面操作 先安装CCleaner, 链接:CCleaner Makes Y…

Salesforce CPQ - 02 - Quote Price

最近又有客户来咨询学习Salesforce CPQ&#xff0c;所以本人总结了下近几年CPQ培训的一些实际案例拿出来分享给大家&#xff1b; 再次介绍下本人是一位Salesforce十多年的从业者。 先来介绍下Salesforce的价格体系&#xff0c;再介绍下各个Product Price是如何配置及使用的&a…

2024 年 5 大移动应用安全预测

2024 年已经到来&#xff0c;企业必须为接下来的事情做好准备。为未来做好准备需要回顾过去。企业可以从那里判断自己当前的状态&#xff0c;从而做出准确的预测。 移动应用程序安全仍然是企业关注的一个重要问题&#xff0c;特别是当消费者依赖应用程序来完成更重要的任务时。…

docker三剑客compose+machine+swarm小结

背景 在容器领域&#xff0c;不少公司会使用docker三剑客composemachineswarm进行容器编排和部署&#xff0c;本文就简单记录下这几个工具的用法 三剑客composemachineswarm compose compose主要是用于容器编排&#xff0c;我们部署容器时&#xff0c;容器之间会有依赖&…

【yolov8部署实战】VS2019环境下使用C++和OpenCV环境部署yolo项目|含详细注释源码

一、前言 之前一阵子一直在做的就是怎么把yolo项目部署成c项目&#xff0c;因为项目需要嵌套进yolo模型跑算法。因为自己也是本科生小白一枚&#xff0c;基本上对这方面没有涉猎过&#xff0c;自己一个人从网上到处搜寻资料&#xff0c;写代码&#xff0c;调试&#xff0c;期间…

Android 基础入门 基础简介

1. 观察App运行日志 2.Android 开发设计的编程语言 koltin Java c c 3.工程目录结构 4.Gradle 5.build.gradle 文件解析 plugins {id("com.android.application")//用了哪些插件 主配置文件版本控制 所以这里不用写版本 }android {namespace "com.tiger.myap…

C#入门:简单数据类型和强制类型转换

本文由 简悦 SimpRead 转码&#xff0c; 原文地址 mp.weixin.qq.com 本期来讲讲 unity 的脚本语言 —C#&#xff0c;C# 的简单数据类型及范围和强制类型转化的方法。这可是 unity 游戏开发必备技能。 1. 简单数据类型 各个类型的范围&#xff1a; byte -> System.Byte (字节…