2-1 C++类的转换函数与禁止隐士转换(explicit)

news2025/7/8 21:43:07

1. 转换函数与explicit关键字

1.1 转换函数

    下述代码的第5行operator double()即是一个转换函数,通过这个函数,编译器可以在需要的情况下,直接将Fraction类型的对象转换为double类型。这个函数有两个特点:首先因为转换函数的返回类型已经通过函数名double()给定,即返回double类型的结果,所以不需要写返回类型;其次,因为这个转换函数并不会更改对象的数据成员,所以将此函数声明为成const
    编译器在执行第17行代码double d = 4+f时,会有很多的判断。其会考虑代码里有没有定义+号的运算符重载函数,且此重载函数接收doublefraction类型的对象作为第一参数与第二参数。若有此函数,则可以直接使用,下述代码是没有写这个重载函数的。编译器还会考虑是否存在f的转换函数,将f转换为double类型来执行相加操作,因为定义了double()转换函数,所以此过程可以执行成功,进而完成相加的操作。

class Fraction {
public:
    Fraction(int num, int den = 1) : m_numerator(num), m_denominator(den) {}

    operator double() const {
        return (double) (m_numerator / m_denominator);
    }

private:
    int m_numerator; //分子
    int m_denominator; //分母

};

int main() {
    Fraction f(3, 5);
    double d = 4 + f;    //调用operator double()将f转为0.6,并执行相加
}

    下述代码第3行的构造函数,虽然有两个参数(parameter),但是其最少只需要接收一个,这种构造函数被称为one argument ctor。这种构造函数比较页数,因为其存在隐式转换的特性。在代码的第15行,3通过调用上述的构造函数可以转换为Fraction类型的匿名对象Fraction(3,1),进而调用重载的+运算符完成加法操作。

class Fraction {
public:
    Fraction(int num, int den = 1) : m_numerator(num), m_denominator(den) {}

    Fraction operator+(const Fraction &f);

private:
    int m_numerator; //分子
    int m_denominator; //

};

int main() {
    Fraction f(3, 5);
    Fraction d2 = f + 3;     //调用隐式转换的构造函数将4转为Fraction(4,1),再调用operator+
}

    上述的分数类已经很好的说明了转换函数的用途,在数学中将一个分数直接转为double类型是一个很自然的事情。
cpp的标准库中也有转换函数的应用,如下是标准库中一个特化vector的模板,当vector存储bool类型时,在空间存储上或处理逻辑上和别的元素类型都有可能有区别,所以这里是特化的vector<bool, Alloc>类型的模板。
    这里vector重载的下标运算符[]可以得到执行下标的元素,但是注意其返回类型并不是bool类型,而是被代理成了_bit_reference类型。但是当用户使用标准库时,其期望的返回类型一定是bool类型,所以这里对_bit_reference类即定义了转换函数,用于将此类型的对象转换为bool类型。

Alt

1.2 no-explicit & one-argument 构造函数

    隐式转换的功能,有时候会导致代码出现二义性,如下代码的第19行,对于这个加法操作,3可以通过构造函数隐式转为Fraction类型完成相加,而f也可以通过转换函数转为double类型完成相加,在+操作的执行过程中会出现二义性。

class Fraction {
public:
    Fraction(int num, int den = 1) : m_numerator(num), m_denominator(den) {}

    operator double() const {
        return double(m_numerator / m_denominator);
    }

    Fraction operator+(const Fraction &f);

private:
    int m_numerator; //分子
    int m_denominator; //

};

int main() {
    Fraction f(3, 5);
    Fraction d2 = f + 3;     
}

    为了消除这种二义性,即禁止编译器的隐式转换,需要在构造函数前加上explict关键字,这时3就不能通过这个构造函数隐式转为double类型。
    除了构造函数外,也可以对转换函数加上explicit关键字,这时这个类就必须通过显式转换的方式转为指定类型:如下所示,当对转换函数加上explicit后,第23行将无法通过编译,而必须通过24、25行的这种显式转换即可。
这里也定义了bool类型的转换函数,可以发现27行是无法通过编译的,而28行在if判断内,编译器会认为这是一个显式转换,不会报错。

class Fraction {
public:
    explicit Fraction(int num, int den = 1) : m_numerator(num), m_denominator(den) {}

    explicit operator double() const {
        return double(m_numerator / m_denominator);
    }

    explicit operator bool() const {
        return m_denominator != 0;
    }

    Fraction operator+(const Fraction &f);

private:
    int m_numerator; //分子
    int m_denominator; //

};

int main() {
    Fraction f(3, 5);
//    cout << (f + 2.0) << endl;
    cout << ((double) f + 2.0) << endl;
    cout << (static_cast<double >(f) + 2.0) << endl;

    // bool j = f;
    if (f) {
    }
}

1.3 c++11后的explicit

    在1.2部分叙述的禁止隐式转换在c++11之前就已经存在,不过其只针对单一参数的构造函数,这种构造函数也可以被称为no-explicit and one-argument 构造函数。在c++11后出现了统一初始化,所以explicit也可以针对多参数的情况:
    若不存在第七行的接收initializer_list的构造函数,则第18行是无法通过编译的。因为第18行的统一初始化方式执行时,编译器将{77, 5, 42}包装成一个initializer_list,因为不存在能够接受initializer_list的构造函数,编译器会将这三个元素依次分解,传递给第10行的构造函数,但是此构造函数禁止隐式转转,所以将无法通过编译。在c++11之后,增加了统一初始化方式,explicit也可以作用于多参的情形了。

class P {
public:
    P(int a, int b) {
        cout << "P(int a,int b)" << endl;
    }

    P(initializer_list<int>) {
        cout << "P(initializer_list<int>)" << endl;
    }
    explicit P(int a, int b, int c) {
        cout << "explicit P(int a,int b,int c)" << endl;
    }
};

int main() {
    P p(77, 5);        //调用P(int a,int b)
    P p2{77, 5};        //调用P(initializer_list<int>)
    P p3{77, 5, 42};      //调用P(initializer_list<int>)
    P p4 = {77, 5, 42};      //调用P(initializer_list<int>)
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

FFplay文档解读-50-多媒体过滤器四

32.21 showspectrumpic 将输入音频转换为单个视频帧&#xff0c;表示音频频谱。 过滤器接受以下选项&#xff1a; size&#xff0c;s指定输出的视频大小。 有关此选项的语法&#xff0c;请查阅&#xff08;ffmpeg-utils&#xff09;视频大小语法。 默认值为4096x2048。 mod…

Spring Boot入门必会(基本介绍+依赖管理+自动装配)

目录 一.基础入门 1.Spring Boot 是什么? 2.SpringBoot 快速入门 2.1完成步骤 2.2快速入门小结 3.Spring SpringMVC SpringBoot 的关系 3.1梳理关系 3.2如何理解 -约定优于配置 二.依赖管理和自动配置 1.依赖管理 1.1 什么是依赖管理 1.2 修改自动仲裁/默认版本号 …

论文阅读-Federated Social Recommendation with Graph NeuralNetwork

基于图神经网络的联邦社交推荐 1. 引言 因此&#xff0c;针对社交推荐任务&#xff0c;我们设计了一个联邦学习推荐系统&#xff0c;该系统具有异构性、个性化和隐私保护要求&#xff0c;具有一定的挑战性。为此&#xff0c;设计了一个基于图神经网络(FeSoG)的联邦社交推荐框…

【RHCE】ansible的简单配置

目录 使用静态清单文件指定受管主机 定义主机清单 定义方式 使用静态主机清单指定受管主机&#xff08;默认&#xff09; 验证清单 第一种方式 第二种方式&#xff08;图表形式显示&#xff09; 选择主机和组&#xff1a; 1>匹配所有主机 2>匹配指定的主机或者主…

二叉树与树、森林之间的转换

关于树的概念 树可以称为特殊的森林 &#xff0c; 其中二叉树是树中一些节点度数最大为2 &#xff0c;并且分左右孩子的树 ● 二叉树很重要 • 结构简单 • 存储效率高 • 运算算法相对简单 • 任何森林、树都可以转换成二叉树 ● 讨论 • 二叉树 度为2 的树 ? 答: 树的度就是…

官方盘点 .NET 7 新功能

.NET 7 为C# 11/F# 7、.NET MAUI、ASP.NET Core/Blazor、Web API、WinForms、WPF 等应用程序带来了更高的性能和新功能。使用 .NET 7&#xff0c;您还可以轻松地将 .NET 7 项目容器化&#xff0c;在 GitHub 操作中设置 CI/CD 工作流&#xff0c;并实现云原生可观察性。欢迎下载…

java之Fork/Join框架

文章目录前言工作窃取算法Fork/Join框架的设计Fork/Join框架的异常处理Fork/Join框架的实现原理总结前言 Fork/Join框架是java7提供的一个用于执行并行任务的框架&#xff0c;是一个把大部分任务分割成若干个小任务&#xff0c;最终汇总每个小任务结果后&#xff0c;得到大任务…

4进程地址空间

文章目录前言1. 概念引入2. CPU和物理内存关系3.何为进程地址空间4. 为什么存在地址空间?前言 本节主要是讲解进程地址空间,区分和物理内存地址空间的差别,并且向读者解释四个疑问: 怎样验证地址空间的排布; 进程地址空间是什么; 进程地址空间和物理内存之间的关系; 为什么要…

[附源码]java毕业设计基于web的球类体育馆预定系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

数据链路层(2层 Data Link Layer)

1、数据链路层属于2层 2、传输单元&#xff1a;帧 帧格式&#xff1a; 802.3 有线网卡 802.11 无线网卡&#xff0c;无线路由器都支持802.11 802开头的都是国际标准&#xff0c;是由IEEE国际学术组织制定的标准 3、帧结构的构成&#xff1a;MAC子层&#xff08;帧头&…

MySQL表的增删改查(进阶)

目录1.数据库约束1.1约束类型1.2 NULL约束1.3 UNIQUE&#xff1a;唯一约束1.4 PRIMARY KEY: 主键约束1.5 DEFAULT&#xff1a;默认值约束1.6 FOREIGN KEY&#xff1a;外键约束2. 表的设计3. 新增4. 查询4.1 聚合查询4.1.2 GROUP BY子句4.2 联合查询4.2.1内连接4.2.2外连接4.2.3…

2022 第十四届蓝桥杯模拟赛第一期(题解与标程)

第十四届蓝桥杯模拟赛第一期1. 二进制位数问题描述答案提交参考答案2. 晨跑问题描述答案提交参考答案3. 调和级数问题描述答案提交参考答案程序验证4. 山谷问题描述答案提交参考答案5. 最小矩阵问题描述答案提交参考答案6. 核酸日期问题描述输入格式输出格式样例输入样例输出评…

项目风险管理的5大关键点,你做了几点?

1、全方位科学分析项目风险 为了提高项目抗风险能力&#xff0c;我们需要对项目风险进行科学全面的分析。一般我们从3个维度对风险进行科学分析&#xff1a;影响的严重性、发生的可能性、产生的影响性。 根据风险或机会对项目的影响程度&#xff0c;一般我们会从三个维度将其划…

javascript大作业《web课程设计》用html做一个期末作业网站,梅西足球体育网页,css

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

STING 与 cGAS的结合导致TBK1 激酶募集和活化

来自细菌或病毒的核酸在受感染的细胞中会产生强效的免疫反应&#xff0c;而病原体衍生核酸的检测是宿主感知感染并启动保护性免疫反应的核心策略。cGAS (Cyclic GMP-AMP synthase) 是一种双链 DNA 传感器&#xff0c;可催化 cGAMP&#xff08;cyclic GMP-AMP&#xff09;的合成…

二级导航栏

简介&#xff1a;本文通过HTML与CSS相集合的方式&#xff0c;来实现二级导航菜单。 HTML构建骨架 <body><ul class"nav1"><li>水果<ul class"nav2"><li>苹果</li><li>香梨</li><li>火龙果</li…

前端CSS射门动画-为梅西最后一届世界杯加油

☆ 距离2022卡塔尔世界杯只有6天时间了&#xff0c;众多球星我喜欢梅西和奥乔亚。 ☆ 我们不能到现场去&#xff0c;只能手中的代码自娱自乐一下&#xff0c;就当为梅西加油了。这是梅西最后一届世界杯了。 梅西给我的感觉&#xff0c;踢球足够利落干净&#xff0c;你不会从他的…

AI遮天传 ML/DL-感知机

感知机的出现是人工智能发展史一大重要里程碑&#xff0c;其后才诞生了&#xff1a;多层感知机、卷积神经网络等一系列的经典网络模型。 在我看来&#xff0c;它虽然是深度学习领域的一大开端&#xff0c;但本身解决的只是线性二分类问题&#xff0c;它本身与机器学习经典模型线…

RORγ 反向激动剂-XY101 小分子化合物

早在 2016 年&#xff0c;中国科学院广州生物医药与健康研究院许永教授团队就曾与加州大学戴维斯分校的陈宏武教授合作&#xff0c;首次发现核激素受体 RORγ 是作用于雄激素受体 AR 上游的关键驱动因子&#xff0c;直接调控雄激素受体 AR 的表达。因此&#xff0c;RORγ 成为前…

gitlab+jenkins+harbor次完整CI链条

用一台机器搭harbor 先安装docker工具&#xff0c;因为装harbor需要docker [rootharbor ~]# cd /etc/yum.repos.d [rootharbor yum.repos.d]# wget https://mirrors.ustc.edu.cn/docker-ce/linux/centos/docker-ce.repo [rootharbor yum.repos.d]# sed -i s#download.docke…