C++ 转换构造函数

在 C/C++ 中,不同的数据类型之间可以相互转换。无需用户指明如何转换的称为自动类型转换(隐式类型转换),需要用户显式地指明如何转换的称为强制类型转换。

自动类型转换示例:

int a = 6;
a = 7.5 + a;

编译器对 7.5 是作为 double 类型处理的,在求解表达式时,先将 a 转换为 double 类型,然后与 7.5 相加,得到和为 13.5。在向整型变量 a 赋值时,将 13.5 转换为整数 13,然后赋给 a。整个过程中,我们并没有告诉编译器如何去做,编译器使用内置的规则完成数据类型的转换。

强制类型转换示例:

int n = 100;
int *p1 = &n;
float *p2 = (float*)p1;

p1 是int *类型,它指向的内存里面保存的是整数,p2 是float *类型,将 p1 赋值给 p2 后,p2 也指向了这块内存,并把这块内存中的数据作为小数处理。我们知道,整数和小数的存储格式大相径庭,将整数作为小数处理非常荒诞,可能会引发莫名其妙的错误,所以编译器默认不允许将 p1 赋值给 p2。但是,使用强制类型转换后,编译器就认为我们知道这种风险的存在,并进行了适当的权衡,所以最终还是允许了这种行为。

不管是自动类型转换还是强制类型转换,前提必须是编译器知道如何转换,例如,将小数转换为整数会抹掉小数点后面的数字,将int *转换为float *只是简单地复制指针的值,这些规则都是编译器内置的,我们并没有告诉编译器。

换句话说,如果编译器不知道转换规则就不能转换,使用强制类型也无用,请看下面的例子:

#include <iostream>
using namespace std;//复数类
class Complex{
public:Complex(): m_real(0.0), m_imag(0.0){ }Complex(double real, double imag): m_real(real), m_imag(imag){ }
public:friend ostream & operator<<(ostream &out, Complex &c);  //友元函数
private:double m_real;  //实部double m_imag;  //虚部
};//重载>>运算符
ostream & operator<<(ostream &out, Complex &c){out << c.m_real <<" + "<< c.m_imag <<"i";;return out;
}int main(){Complex a(10.0, 20.0);a = (Complex)25.5;  //错误,转换失败return 0;
}

25.5 是实数,a 是复数,将 25.5 赋值给 a 后,我们期望 a 的实部变为 25.5,而虚部为 0。但是,编译器并不知道这个转换规则,这超出了编译器的处理能力,所以转换失败,即使加上强制类型转换也无用。

C++ 允许我们自定义类型转换规则,用户可以将其它类型转换为当前类类型,也可以将当前类类型转换为其它类型。这种自定义的类型转换规则只能以类的成员函数的形式出现,换句话说,这种转换规则只适用于类。

转换构造函数

将其它类型转换为当前类类型需要借助转换构造函数(Conversion constructor)。转换构造函数也是一种构造函数,它遵循构造函数的一般规则。转换构造函数只有一个参数。

以 Complex 类为例,我们为它添加转换构造函数:

#include <iostream>
using namespace std;//复数类
class Complex{
public:Complex(): m_real(0.0), m_imag(0.0){ }Complex(double real, double imag): m_real(real), m_imag(imag){ }Complex(double real): m_real(real), m_imag(0.0){ }  //转换构造函数
public:friend ostream & operator<<(ostream &out, Complex &c);  //友元函数
private:double m_real;  //实部double m_imag;  //虚部
};//重载>>运算符
ostream & operator<<(ostream &out, Complex &c){out << c.m_real <<" + "<< c.m_imag <<"i";;return out;
}int main(){Complex a(10.0, 20.0);cout<<a<<endl;a = 25.5;  //调用转换构造函数cout<<a<<endl;return 0;
}

运行结果:

10 + 20i
25.5 + 0i

Complex(double real);就是转换构造函数,它的作用是将 double 类型的参数 real 转换成 Complex 类的对象,并将 real 作为复数的实部,将 0 作为复数的虚部。这样一来,a = 25.5;整体上的效果相当于:

a.Complex(25.5);

将赋值的过程转换成了函数调用的过程。

在进行数学运算、赋值、拷贝等操作时,如果遇到类型不兼容、需要将 double 类型转换为 Complex 类型时,编译器会检索当前的类是否定义了转换构造函数,如果没有定义的话就转换失败,如果定义了的话就调用转换构造函数。

转换构造函数也是构造函数的一种,它除了可以用来将其它类型转换为当前类类型,还可以用来初始化对象,这是构造函数本来的意义。下面创建对象的方式是正确的:

Complex c1(26.4);  //创建具名对象
Complex c2 = 240.3;  //以拷贝的方式初始化对象
Complex(15.9);  //创建匿名对象
c1 = Complex(46.9);  //创建一个匿名对象并将它赋值给 c1

在以拷贝的方式初始化对象时,编译器先调用转换构造函数,将 240.3 转换为 Complex 类型(创建一个 Complex 类的匿名对象),然后再拷贝给 c2。

如果已经对+运算符进行了重载,使之能进行两个 Complex 类对象的相加,那么下面的语句也是正确的:

Complex c1(15.6, 89.9);
Complex c2;
c2 = c1 + 29.6;
cout<<c2<<endl;

在进行加法运算符时,编译器先将 29.6 转换为 Complex 类型(创建一个 Complex 类的匿名对象)再相加。

注意:为了获得目标类型,编译器会“不择手段”,会综合使用内置的转换规则和用户自定义的转换规则,并且会进行多级类型转换,例如:
编译器会根据内置规则先将 int 转换为 double,再根据用户自定义规则将 double 转换为 Complex(int --> double --> Complex);

编译器会根据内置规则先将 char 转换为 int,再将 int 转换为 double,最后根据用户自定义规则将 double 转换为 Complex(char --> int --> double --> Complex)。

从本例看,只要一个类型能转换为 double 类型,就能转换为 Complex 类型。请看下面的例子:

int main(){Complex c1 = 100;  //int --> double --> Complexcout<<c1<<endl;c1 = 'A';  //char --> int --> double --> Complexcout<<c1<<endl;c1 = true;  //bool --> int --> double --> Complexcout<<c1<<endl;Complex c2(25.8, 0.7);//假设已经重载了+运算符c1 = c2 + 'H' + true + 15;  //将char、boolint都转换为Complex类型再运算cout<<c1<<endl;return 0;
}

运行结果:

100 + 0i
65 + 0i
1 + 0i
113.8 + 0.7i

构造函数

构造函数的本意是在创建对象的时候初始化对象,编译器会根据传递的实参来匹配不同的(重载的)构造函数

1 默认构造函数。就是编译器自动生成的构造函数。以 Complex 类为例,它的原型为:

Complex();  //没有参数

2 普通构造函数。就是用户自定义的构造函数。以 Complex 类为例,它的原型为:

Complex(double real, double imag);  //两个参数

3 拷贝构造函数。在以拷贝的方式初始化对象时调用。以 Complex 类为例,它的原型为:

Complex(const Complex &c);

4 转换构造函数。将其它类型转换为当前类类型时调用。以 Complex 为例,它的原型为:

Complex(double real);

不管哪一种构造函数,都能够用来初始化对象,这是构造函数的本意。假设 Complex 类定义了以上所有的构造函数,那么下面创建对象的方式都是正确的:

Complex c1();  //调用Complex()
Complex c2(10, 20);  //调用Complex(double real, double imag)
Complex c3(c2);  //调用Complex(const Complex &c)
Complex c4(25.7);  //调用Complex(double real)

这些代码都体现了构造函数的本意——在创建对象时初始化对象。

除了在创建对象时初始化对象,其他情况下也会调用构造函数,例如,以拷贝的的方式初始化对象时会调用拷贝构造函数,将其它类型转换为当前类类型时会调用转换构造函数。这些在其他情况下调用的构造函数,就成了特殊的构造函数了。特殊的构造函数并不一定能体现出构造函数的本意。

Complex 类的精简

上面的 Complex 类中我们定义了三个构造函数,其中包括两个普通的构造函数和一个转换构造函数。其实,借助函数的默认参数,我们可以将这三个构造函数简化为一个,请看下面的代码:

#include <iostream>
using namespace std;//复数类
class Complex{
public:Complex(double real = 0.0, double imag = 0.0): m_real(real), m_imag(imag){ }
public:friend ostream & operator<<(ostream &out, Complex &c);  //友元函数
private:double m_real;  //实部double m_imag;  //虚部
};//重载>>运算符
ostream & operator<<(ostream &out, Complex &c){out << c.m_real <<" + "<< c.m_imag <<"i";;return out;
}int main(){Complex a(10.0, 20.0);  //向构造函数传递 2 个实参,不使用默认参数Complex b(89.5);  //向构造函数传递 1 个实参,使用 1 个默认参数Complex c;  //不向构造函数传递实参,使用全部默认参数a = 25.5;  //调用转换构造函数(向构造函数传递 1 个实参,使用 1 个默认参数)return 0;}

精简后的构造函数包含了两个默认参数,在调用它时可以省略部分或者全部实参,也就是可以向它传递 0 个、1 个、2 个实参。转换构造函数就是包含了一个参数的构造函数,恰好能够和其他两个普通的构造函数“融合”在一起。

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

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

相关文章

android 媒体库扫描,如何扫描出Android系统媒体库中视频文件

Android系统启动时会去扫描系统文件&#xff0c;并将系统支持的视频文件(mp4,3gp,wmv)扫描到媒体库(MediaStore)中&#xff0c;下面代码演示如何获得这些文件的信息&#xff1a;publicstatic List sysVideoList null;// 视频信息集合sysVideoList new ArrayList();setVideoLi…

C++ 四种类型转换运算符

隐式类型转换是安全的&#xff0c;显式类型转换是有风险的&#xff0c;C语言之所以增加强制类型转换的语法&#xff0c;就是为了强调风险&#xff0c;让程序员意识到自己在做什么。 但是&#xff0c;这种强调风险的方式还是比较粗放&#xff0c;粒度比较大&#xff0c;它并没有…

Android leak内存,GitHub - jin870132/memoryleakdemo: 安卓内存泄露几种常见形式及解决方案...

安卓内存泄露几种常见形式及解决方案一.前言1.内存溢出与内存泄露内存溢出(oom)&#xff0c;是指程序在申请内存时&#xff0c;没有足够的内存空间供其使用&#xff0c;出现oom&#xff1b;比如申请了一个integer,但给它存了long才能存下的数&#xff0c;那就是内存溢出。内存泄…

第二批鸿蒙手机排行,鸿蒙系统第二批升级机型有哪些 鸿蒙系统第二批升级机型名单一览...

华为6月2日召开开启鸿蒙发布会&#xff0c;很多华为手机的用户都想第一时间用上鸿蒙手机系统&#xff0c;今天就给大家带来鸿蒙系统第二批升级机型名单一览&#xff0c;一起来看看吧鸿蒙系统第二批升级机型名单一览具体机型&#xff1a;HUAWEI Mate20 SeriesHUAWEl nova 8 Seri…

C++ 异常类型以及多级catch匹配

exceptionType是异常类型&#xff0c;它指明了当前的 catch 可以处理什么类型的异常&#xff1b;variable是一个变量&#xff0c;用来接收异常信息。当程序抛出异常时&#xff0c;会创建一份数据&#xff0c;这份数据包含了错误信息&#xff0c;程序员可以根据这些信息来判断到…

火狐 html5 退出 白屏,Html5+ 后退按钮出现白屏(webView.back会白屏)

您好&#xff0c;打包装到 iPad 上去调试&#xff0c;A ->B 之后&#xff0c;第一次调用 webView.back 会显示白屏&#xff0c;页面切换使用的 webView.loadUrl &#xff0c;代码如下&#xff1a;var sub plus.webview.create(_basePath pages/canlucate/canlucate.html, …

C++ throw

我们知道C 异常处理的流程&#xff0c;具体为&#xff1a; 抛出&#xff08;Throw&#xff09;--> 检测&#xff08;Try&#xff09; --> 捕获&#xff08;Catch&#xff09;异常必须显式地抛出&#xff0c;才能被检测和捕获到&#xff1b;如果没有显式的抛出&#xff0…

html移除click事件绑定,带你了解JQuery中绑定事件(bind())和移除事件(unbind())...

本文主要向大家详细介绍了jQuery的绑定事件和移除事件的使用方法和示例分享&#xff0c;这里推荐给有需要的小伙伴们参考下。有时候事件执行完了&#xff0c;想取消事件的效果可以通过一定的办法来处理。比如bind()(绑定事件)和unbind()(移除通过bind()方法添加的事件)方法来移…

html怎么设计自己的网页,求一份自己设计的简单网页 HTML格式

A&#xff1a;百格*特点&#xff1a;该仪器用于均匀划出一定规格尺寸的方格&#xff0c;通过评定方格内涂膜的完整程度来评定涂膜对基材附着程度&#xff0c;以‘级’表示。它主要用于有机涂料划格法附着力的测定&#xff0c;不仅适用于实验室&#xff0c;也可用于各种条件下的…

VC2010 项目的创建

在VC2010中创建一个项目 1 . 创建新项目。打开我们的VC2010&#xff0c;点工具栏第一个按钮&#xff08;New Project&#xff09;&#xff0c;或者菜单 File -> New -> Project…&#xff0c;或者按快捷键 CtrlShiftN&#xff0c;几种方式都可以。 2 . 在 “New Project…

html5教学案例撰写,怎样撰写教育教学案例

怎样撰写教育教学案例教学是教师的教和学生的学所组成的一种人类特有的人才培养活动。那么&#xff0c;怎样撰写教育教学案例呢?下面是小编收集整理的撰写教育教学案例的相关内容&#xff0c;希望对您有所帮助!1.撰写教育教学案例的思想准备要写好教育、教学案例&#xff0c;首…

error C2143: syntax error : missing ';' before '}'

我们在运行C程序的时候经常会遇到错误&#xff0c;如果你遇到了这个错误&#xff1a;error C2143: syntax error : missing ‘;’ before ‘}’&#xff0c;那麽我将帮你解决这个错误。 错误展示 完整代码 #include <stdio.h> #define exchange(a,b){int t; ta;ab;bt} …

绘制彩虹html代码,HTML5 Canvas 彩虹螺旋图生成器

JavaScript语言&#xff1a;JaveScriptBabelCoffeeScript确定$(function() {var myCanvas, context, width, height;var lines [],numberOfLines 12;var colours [#FFD800, #FF6A00, #FF0000, #0094FF, #0026FF, #4800FF, #7FFF8E, #B6FF00, #4CFF00, #FFFFFF];var Line fu…

VC2010运行C程序时黑框一闪就没

黑框一闪就没如何解决的呢&#xff1f; 首先我们要知道为什么黑框一闪就没 闪一下是因为它执行完输出函数(printf)后直接返回系统了。 解决办法 在程序里加一个system(“pause”)&#xff0c;这个是调用系统函数&#xff0c;到时候会显示"按任意键退出"。 使用方…

厦门大学计算机科学与技术学院考研分数线,2020年厦门大学计算机科学与技术考研经验分享...

原标题&#xff1a;2020年厦门大学计算机科学与技术考研经验分享大家好&#xff0c;我是育明考研小赵老师关于2020年厦门大学计算机科学与技术考研信息汇总&#xff0c;请参考一、院校介绍厦门大学(Xiamen University)&#xff0c;简称厦大(XMU)&#xff0c;是中华人民共和国教…

Redis ops详解

Redis缓存数据库的ops问题 我们使用Java操作Redis数据库的时候&#xff0c;往往会输出和ops相关的内容&#xff0c;下面给大家讲解一下ops相关的内容。 ops是什么&#xff1f; redis中的OPS 即operation per second 每秒操作次数。意味着每秒对Redis的持久化操作。 所以我们…

山东大学继续教育计算机在线作业,山东大学继续教育数文字电子技术基础习题3及答案.docx...

精品文档精品文档PAGEPAGE5精品文档PAGE..数字电子技术基础模拟卷3一填空。1逻辑代数中&#xff0c;基本的运算关系是与、或和非。2十进制数27转换成二进制数为11011&#xff1b;转换成8421BCD码是3在逻辑代数中&#xff0c;AABAB&#xff1b;A1。4同一个逻辑函数可以有不同的逻…

Spring操作Redis

在 Spring 中使用 Redis&#xff0c;除了需要 jedis.jar 外&#xff0c;还需要下载 spring-data-redis.jar&#xff0c;这里值得注意的是 jar 包和 Spring 版本兼容的问题&#xff0c;我使用的 jar 包版本是 1.8.1&#xff0c;而 Spring 的版本是 5.0.4&#xff0c;如果使用其他…

考研规划计算机科学与技术,2021考研:计算机科学与技术研究方向及冲刺复习规划...

一、研究方向20数据挖掘技术及应用21智能软件与计算理论22模式识别与图像处理23数据库理论及其应用技术24软件工程与面向对象设计&#xff0c;二、初试科目①101思想政治理论②201英语一③301数学一④408计算机学科专业基础综合三、考试内容和试卷结构数据结构、计算机组成原理…

Redis的6种数据类型

Redis 是一种基于内存的数据库&#xff0c;并且提供一定的持久化功能&#xff0c;它是一种键值&#xff08;key-value&#xff09;数据库&#xff0c;使用 key 作为索引找到当前缓存的数据&#xff0c;并且返回给程序调用者。 当前的 Redis 支持 6 种数据类型&#xff0c;它们…