当前位置: 首页 > news >正文

explicit 的作用(如何避免编译器进行隐式类型转换)

目录

1. 隐式转换(Implicit Conversion)

2. 显式转换(Explicit Conversion)

3. 隐式转换的风险与显式转换的必要性

4. 隐式类型转换的例子

5. explicit 的作用

6. explicit 在构造函数中的作用

7. explicit 适用于转换操作符


  • 隐式类型转换 发生在类的单参数构造函数和类型转换操作符被自动调用的情况下。
  • explicit 的作用:阻止编译器进行隐式类型转换,要求调用者显式地进行类型转换或对象创建。
  • 使用场景:在类的单参数构造函数和类型转换操作符中,当隐式转换可能引发错误或误解时,使用 explicit 关键字可以提高代码的可读性和安全性。

在说明explicit 的作用之前,我们先来搞清楚什么是隐式转换,什么是显示转换。

隐式转换显式转换是两种类型转换方式,它们用于将一种类型的值转换为另一种类型。在编程语言中(如 C++、Java、Python 等),这两者的区别主要体现在转换的自动性控制权上。

explicit 是 C++ 中的关键字,用于防止编译器在某些情况下进行隐式类型转换,尤其是在构造函数和类型转换操作符中。它可以显式声明构造函数或类型转换,避免潜在的意外行为,从而提高代码的安全性和可读性。

1. 隐式转换(Implicit Conversion)

隐式转换是由编译器自动执行的类型转换。通常,当需要将一个类型的值转换为兼容的另一种类型时,编译器会隐式地进行这种转换。它不需要显式的操作或用户的干预。

特点:

  • 自动执行:不需要程序员明确地进行转换,编译器在适当的时候自动完成。
  • 安全性:隐式转换通常在转换不会丢失数据或引起精度问题时发生。
  • 简化代码:减少了类型转换的显式操作,使代码更加简洁。

常见的隐式转换:

  • 整数到浮点数的转换。
  • 窄类型到宽类型的转换,例如 intdouble
  • 从派生类到基类的转换(向上转型)。

C++ 示例:

int a = 10;
double b = a;  // 隐式转换:int 转换为 double

在这个例子中,a 被隐式地转换为 double 类型赋值给 b

Python 示例:

a = 10
b = a + 1.5  # 隐式转换:int 转换为 float

a 在进行加法操作时,自动转换为 float 类型。

2. 显式转换(Explicit Conversion)

显式转换是由程序员手动指定的类型转换方式。程序员必须通过特定的语法,明确告诉编译器执行转换操作。这种转换通常用于转换不兼容的类型,或者当隐式转换可能会导致数据丢失时。

特点:

  • 需要手动指定:程序员必须使用明确的语法进行类型转换。
  • 安全性控制:显式转换通常用于需要精确控制转换过程,避免潜在的错误或数据丢失。
  • 灵活性:可以进行更复杂和不安全的类型转换。

常见的显式转换方式:

  • C++ 中的类型转换操作:如 static_castdynamic_castreinterpret_cast
  • C 风格的类型转换:如 (int)(double)
  • Python 中的强制类型转换:如 int()float()str()

C++ 示例:

double x = 9.7;
int y = static_cast<int>(x);  // 显式转换:double 转换为 int,截断小数部分

Python 示例:

x = 9.7
y = int(x)  # 显式转换:float 转换为 int,截断小数部分

在这两个示例中,x 被显式转换为 int,而转换后的小数部分会被舍弃。

隐式转换 vs 显式转换

3. 隐式转换的风险与显式转换的必要性

隐式转换虽然方便,但有时会引发意外的错误,特别是当类型不完全兼容时,可能会导致数据丢失或精度问题。这时候,显式转换就非常有用,可以确保程序员明确意识到可能的风险并加以控制。

风险示例:

double d = 9.99;
int i = d;  // 隐式转换,数据丢失,i 变成 9

这种情况下,小数部分被丢失。如果我们希望精确控制这种行为,应该使用显式转换:

int i = static_cast<int>(d);  // 显式转换,确保数据截断是程序员预期的行为

接下来我们再来说明explicit 的作用。

4. 隐式类型转换的例子

当一个类有一个带单参数的构造函数时,编译器会自动进行隐式类型转换,将参数类型转换为该类的对象。这种隐式转换有时会导致意想不到的结果。

示例:

#include <iostream>class MyClass {
public:// 构造函数,可以接受 int 类型MyClass(int x) {std::cout << "MyClass constructor called with " << x << std::endl;}
};void printObject(const MyClass& obj) {std::cout << "Object received" << std::endl;
}int main() {MyClass obj = 42;  // 隐式转换:int 转换为 MyClass 对象printObject(42);   // 隐式转换:int 被转换为 MyClass 对象
}

在上面的代码中:

  • MyClass obj = 42; 触发了隐式类型转换,编译器将 42 转换为 MyClass 对象。
  • printObject(42); 也触发了隐式类型转换,将 42 转换为 MyClass 对象。

虽然这种隐式转换看似方便,但在某些情况下可能会导致难以发现的错误或非预期的行为。

5. explicit 的作用

为防止不必要的隐式转换,可以在构造函数前加上 explicit 关键字。explicit 使得构造函数不能被隐式调用,只能通过显式地传递参数来调用。

示例:

#include <iostream>class MyClass {
public:// 使用 explicit 防止隐式类型转换explicit MyClass(int x) {std::cout << "MyClass constructor called with " << x << std::endl;}
};void printObject(const MyClass& obj) {std::cout << "Object received" << std::endl;
}int main() {// MyClass obj = 42;  // 错误!不能进行隐式类型转换MyClass obj(42);     // 必须显式调用构造函数// printObject(42);   // 错误!需要显式转换printObject(MyClass(42));  // 必须显式地转换为 MyClass 对象
}

在此例中,由于使用了 explicit,编译器不再允许隐式类型转换,必须通过显式地构造对象来使用 MyClass

6. explicit 在构造函数中的作用

  • 隐式转换:当一个类有一个带单参数的构造函数时,可以通过传递该类型的参数直接创建对象,而无需显式调用构造函数,这称为隐式类型转换。
  • 使用 explicit:为构造函数添加 explicit 关键字可以阻止编译器执行隐式类型转换,要求调用者显式地进行类型转换。

代码对比:

  • 没有 explicit

class MyClass {
public:MyClass(int x) {// 构造函数}
};MyClass obj = 10;  // 隐式转换
  • 没有 explicit

class MyClass {
public:explicit MyClass(int x) {// 构造函数}
};MyClass obj(10);  // 显式调用

7. explicit 适用于转换操作符

除了构造函数外,explicit 也可以用于类型转换操作符,防止不合适的自动类型转换。

示例:

#include <iostream>class MyClass {
public:explicit operator int() const {return 42;}
};int main() {MyClass obj;// int x = obj;  // 错误!explicit 禁止隐式转换int x = static_cast<int>(obj);  // 正确,必须显式转换std::cout << x << std::endl;  // 输出 42
}

此例中,operator int() 被声明为 explicit,因此不能隐式转换为 int,但可以通过 static_cast 显式转换。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 并发编程:synchronized 关键字
  • 【Linux】Linux 可重入函数
  • 0.ffmpeg面向对象oopc
  • 项目实战系列三: 家居购项目 第五部分
  • C++ STL-Map容器从入门到精通详解
  • HarmonyOs DevEco Studio小技巧9--翻译软件
  • 怎么利用XML发送物流快递通知短信
  • qml Component 组件
  • 【设计模式】设计模式的八大原则
  • 无线麦克风哪个品牌音质最好?十大音质最好的麦克风品牌推荐
  • Lua5.3 参考手册
  • C++(一)----C++基础
  • 用CSS 方式设置 table 样式
  • 抢鲜体验 PolarDB PG 15 开源版
  • AI智能工牌:告别手动录入,1小时上门服务报告,3分钟生成
  • 【译】JS基础算法脚本:字符串结尾
  • 《Javascript数据结构和算法》笔记-「字典和散列表」
  • 【前端学习】-粗谈选择器
  • C# 免费离线人脸识别 2.0 Demo
  • Laravel 菜鸟晋级之路
  • NSTimer学习笔记
  • PHP变量
  • Redash本地开发环境搭建
  • Spring Cloud(3) - 服务治理: Spring Cloud Eureka
  • XForms - 更强大的Form
  • 机器人定位导航技术 激光SLAM与视觉SLAM谁更胜一筹?
  • 解决jsp引用其他项目时出现的 cannot be resolved to a type错误
  • 巧用 TypeScript (一)
  • 正则表达式
  • 3月27日云栖精选夜读 | 从 “城市大脑”实践,瞭望未来城市源起 ...
  • Spring第一个helloWorld
  • ​直流电和交流电有什么区别为什么这个时候又要变成直流电呢?交流转换到直流(整流器)直流变交流(逆变器)​
  • #、%和$符号在OGNL表达式中经常出现
  • (2015)JS ES6 必知的十个 特性
  • (6)添加vue-cookie
  • (C++)栈的链式存储结构(出栈、入栈、判空、遍历、销毁)(数据结构与算法)
  • (CPU/GPU)粒子继承贴图颜色发射
  • (function(){})()的分步解析
  • (NSDate) 时间 (time )比较
  • (仿QQ聊天消息列表加载)wp7 listbox 列表项逐一加载的一种实现方式,以及加入渐显动画...
  • (十一)c52学习之旅-动态数码管
  • (原創) 如何解决make kernel时『clock skew detected』的warning? (OS) (Linux)
  • (转)Windows2003安全设置/维护
  • .apk文件,IIS不支持下载解决
  • .mysql secret在哪_MYSQL基本操作(上)
  • .NET 8 中引入新的 IHostedLifecycleService 接口 实现定时任务
  • .Net中间语言BeforeFieldInit
  • :如何用SQL脚本保存存储过程返回的结果集
  • [ 英语 ] 马斯克抱水槽“入主”推特总部中那句 Let that sink in 到底是什么梗?
  • [20190401]关于semtimedop函数调用.txt
  • [2024-06]-[大模型]-[Ollama]- WebUI
  • [AUTOSAR][诊断管理][ECU][$37] 请求退出传输。终止数据传输的(上传/下载)
  • [AutoSar]状态管理(五)Dcm与BswM、EcuM的复位实现
  • [C#]C#学习笔记-CIL和动态程序集
  • [dfs搜索寻找矩阵中最长递减序列]魔法森林的秘密路径