C++使用类

运算符重载

C++允许将运算符重载扩展到用户定义的类型,C++将会根据操作数的数目和类型来决定使用哪种操作,运算符重载的格式如下:

operatorop(argumet-list)

即由关键字operator,运算符op,()里的参数列表三部分组成,op必须是有效的C++运算符

例如:operator+(),这里代表着要重载+运算符

我们可以通过一个例子来看看运算符重载的便利

我们不使用运算符重载进行两个对象的计算

#include<iostream>using namespace std;class Time
{private:int hours;int minutes;public:Time(){hours = minutes = 0;}Time(int h, int m = 0){hours = h;minutes = m;}Time Sum(const Time &t) const//Time operator+(const Time &t) const{Time sum;sum.minutes = minutes + t.minutes;sum.hours = hours + t.hours + sum.minutes / 60;sum.minutes %= 60;return sum;}void show(){cout<<"hours: "<<hours<<"minutes: "<<minutes<<endl;}
};
int main()
{Time a(10,55);Time b(1,20);a.show();b.show();Time c = a.Sum(b);c.show();return 0;
}

这样就只能通过常规的函数方法来进行求和,即必须通过访问成员函数的方式,而我们使用运算符重载时

我们就能够直接使用+运算符进行计算

我们也可以将operate+()当做一个函数来理解

两者的运算结果都是相等的

总之

operator+()函数的名称使得使用函数表示法或者运算符表示法来调用它,编译器会根据操作数的类型来决定如果进行调用

注意:

在上述的代码中,Sum函数的返回值不能是一个引用,因为我们将引用作为参数是为了提高效率,如果按值传递Time对象,两种的功能相同,但是传递引用,速度更快,使用的内存更少,但是返回值却不能是引用,因为函数会创建一个新的对象来表示两个对象的和,返回对象将会创建对象的副本,则函数调用可以使用它,如果传递的是对象的引用,因为我们定义的对象sum是局部变量,这个变量会在函数结束时被删除,因此引用会指向一个不存在的对象

这样的结果是会导致编译器发生警告

运算符重载限制

C++对用户自定义的运算符重载是有着一定的限制

1.重载后的运算符必须至少有一个操作数是用户自定义的类型,这将会防止用户为标准类型重载运算符,例如将减法运算符重载为计算两个整型的和

2.使用运算符不能违反原来的句法规则,例如不能将%运算符重载为只使用一个操作数,也不能修改运算符的优先级,就像我们上面的例子,将+运算符重载为两个类的相加,得到的新运算符与原来的+具有同样的优先级

3.不能创造新的运算符

4.部分的运算符不允许重载(sizeof运算符,.成员运算符,.*成员指针运算符,::作用域解析运算符,?:条件运算符等)

5.大部分的运算符可以通过成员函数或者非成员函数进行重载,例如

注意:

我们使用非成员函数时是访问不到私有的n的,只能将其改为公有的部分或者通过成员函数才能进行访问,这里是将其改为公有部分去访问(这样做非常不好,尽量不要这么去使用)

但是( =赋值运算符,()函数调用运算符,[]下标运算符,->通过指针访问类成员运算符)只能通过成员函数进行重载

我们来看一串代码,就可以知道为什么=赋值运算符,()函数调用运算符,[]下标运算符,->通过指针访问类成员运算符只能通过成员函数进行重载

这个代码的输出结果为

我们对这个代码进行分析一下:

在main函数中,我们先使用Num创建了一个对象a,此时我们并没有对其进行初始化,因此会调用默认构造函数,进而打印出No,但是,第二条语句a=10好像是把我们赋值运算符的功能进行了重载,否则的话,这条语句就是尝试将一个整型的常量去赋值给一个Num类的对象,这会导致类型不匹配,但是我们编译却没有出错,因此,我们可以得知,当我们的类中没有定义赋值运算符重载的功能时,但是在代码中出现了赋值运算,程序就会调用与赋值运算符右侧值类型匹配的构造函数,因此输出的结果为YES,此时我们可以将a=10就理解为一个a(10)的操作

但是,当我们的类里有运算符重载的操作时,程序就只会去调用我们定义的重载函数

此时我们就编写了一个赋值运算符重载的函数,当出来赋值操作时,编译器就会去调用我们编写的函数,而不是再去调用参数类型匹配的构造函数

注意:

如果我们不在类里面去定义一个赋值运算符重载的函数,而是定义了一个非成员函数的赋值运算符重载的函数,这样会导致编译器报错,因为有构造函数也可以满足我们的赋值操作,编译器不知道要去调用哪一个函数,而加号运算符不会出现这样的操作,因为它们不会去调用构造函数,这就是为什么=赋值运算符,()函数调用运算符,[]下标运算符,->通过指针访问类成员运算符)只能通过成员函数进行重载的原因

友元

友元的介绍

C++控制对于类对象私有部分的访问,通常,公有类方法提供是唯一的访问途径,但是有时候这种限制太过严格,以至不适用于某些编程问题,在这种特定的情况下,C++提供了另外一种形式的访问权限——友元,友元有三种

友元函数

友元类

友元成员函数

通过让函数成为类的友元,可以赋予该函数与类成员函数相同的访问权限,即可以访问到类的私有部分的成员

为什么需要友元

在为类重载二元运算符时,通常需要友元,例如下面这个代码

我们上面进行了+运算符的重载,在这个基础上再加上*运算符的重载

#include<iostream>using namespace std;class Time
{private:int hours;int minutes;public:Time(){hours = minutes = 0;}Time(int h, int m = 0){hours = h;minutes = m;}//Time Sum(const Time &t) constTime  operator+(const Time &t) const{Time sum;sum.minutes = minutes + t.minutes;sum.hours = hours + t.hours + sum.minutes / 60;sum.minutes %= 60;return sum;}Time operator*(double num){Time result;long total = hours * num *60 + minutes * num;result.minutes = total % 60;result.hours = total / 60;return result;void show(){cout<<"hours: "<<hours<<" minutes: "<<minutes<<endl;}
};int main()
{Time a(1,30);Time b(1,20);Time d;Time c;a.show();b.show();d = a + b;c = b * 2.0;c.show();
}

在这个例子中,加法运算符的重载结合的是两个Time值,而乘法运算符将一个Time类的对象与一个double类型的值进行了结合,这就限制了该运算符的使用方式,左侧的操作数b是调用对象

c = b * 2.0;

将会被转化为

c = b.operator*(2.0);

那么如果出现了 c = 2.0 * b;这条语句会发生什么呢?

从概念上来说,b*2.0与2.0*b应该是相同的,但是第一个表达式的值不对应与成员函数,因此2.0不是Time类的对象,因此,编译器不能使用成员函数调用来替换这个表达式,如果使用这样进行编译时,编译器会进行明确的报错

解决这个问题的方法

1.告知所有人只能按照b*2.0这样的格式编写,但是这样对我们并不友好

2.再编写一个非成员函数,让2.0*b这种类型也能匹配,但是这样又会出现一个问题,即我们非成员函数无法去访问我们的私有成员,例如下图

定义了一个非成员函数,但是在非成员函数里尝试去访问Time类中的私有成员,这样编译器就会发出警告,消除警告可以像我们上面所说的将私有成员该为公有成员(非常不好)

由于定义非成员函数不能直接去访问类里面的私有成员,那么我们应该怎么让非成员函数也能够去访问类的私有成员呢?

使用友元函数,就可以去访问类的私有成员

创建友元

友元函数的创造步骤如下

1.将函数的原型放在类声明中,并在声明前加上关键字friend

2.编写函数定义,因为不是类成员函数,因此不需要加上类和作用域解析运算符,此外,在函数定义中也不需要加上关键字friend

按照我们上述的问题,编写能够匹配2.0*b的非成员函数,定义为友元函数

在类的声明中

非成员函数的定义

常用友元:重载<<运算符

一个很有用的类的特性是,可以对<<运算符进行重载,使之能和cout一起来显示对象的内容,例如我们前面使用过的成员函数show(),假如我们有一个Time类的对象tip,如果我们可以使用下面这种方式来显示会比使用show来显示更好

cout<<trip;

我们可以这样做是因为<<是可以被重载的运算符,最初<<运算符是C和C++的位运算符符,ostream类对这个运算符进行了重载,将其转化为了一个输出工具,而cout是一个ostream类的对象,它是智能的,能够识别所有的C++基本类型,因为对于每种基本类型来说,ostream类声明中都包含了相对应的重载的operator<<()定义,因此,要使cout能够识别Time对象,一种方法就是讲一个新的函数运算符定义添加到ostream类声明中,但是这样非常不好,这样会在标准接口上浪费时间,相反,通过Time类声明来让Time类知道如何使用cout,即在Time类里面进行重载,方法如下所示:

1.<<的第一种重载

在Time类中声明

定义函数

这样就可以使用cout<<trip

2.<<的第二种重载

我们上面所写的第一种重载无法进行连续的输出,例如

这样编译器会发出警告

因为C++从左往右读取输出语句,例如int x = 1, y = 2;

cout<<x<<y;等同于(cout<<x)<<y;在这里(cout<<x)的返回值类型还是cout类型,因此还能继续输出结果,而上面定义的输出的结果是void类型,因此无法满足连续的输出,为了保证我们能够连续的输出,我们就要对第一种重载的返回值进行更改.

声明在Time类中

定义operator<<()

调用函数

在我们定义函数时,我们可以不像之前的代码一样,先定义一个临时的类的对象,对其进行操作之后再返回,而是直接使用构造函数去返回,这样写比较简洁,即如下

这样相当于创建了一个无名的新对象,并返回了该对象的副本,这就确保了新的Vector对象是根据构造函数制定的标准规则创建的

提示:

如果方法通过计算得到一个新的类对象,则应该考虑是否可以使用构造函数来完成这个工作,这样可以避免麻烦,并且确保新的对象是按照正确的方式创建的

类的自动类型转换和强制转换

自动类型转换

我们通过下面这个例子来看看C++类的自动转换

在这个程序中,我们定义了一个Sto类,接下来我们使用这个类来创造一个对象a和b,但是在运用时却出现了b = 136.2,在这条语句中,程序将自动使用Sto(double n2)这个构造函数来创建一个临时的对象,随后采用逐成员赋值的方式将该临时对象的内容复制到b中,这一个过程是隐式进行的,因此不需要进行强制类型转换

C++新增的关键字explicit可以关闭类的自动类型转换

 只需要在构造函数前加上explicit关键字,这样关闭隐式的转换之后,再出现这样的赋值语句就会出错

转换函数

我们在类的自动转换中将一个数字赋值给我们类的对象,我们在这里还可以进行相反的操作,即将一个类的对象赋值给我们的变量,这需要使用到转换函数

转换函数是用户自定义的强制类型转换,可以像使用强制类型转换那样去使用它们

转换函数的格式如下:

operator 要转换的类型();

注意:

1.转换函数必须是类方法

2.转换函数不能指定返回类型,因为要转换的类型指出了我们要将类转换为什么类型,因此不需要指定返回

3.转换函数不能有参数,因为转换函数是类方法,它需要通过类对象来调用,从而告知了函数要转换的值,因此不需要参数

我们按照上面的例子定义转换函数并调用

 当出现强制类型转换语句时,程序就会去找相匹配的转换函数

在C++11中,可以对转换函数使用explicit关键字,这样就只能进行显式的类型转换,隐式的类型转换就不能发生

总结

C++为下面的类提供了类型转换

1.只有一个参数的类构造函数将会用于将类型与参数相同的值转换为类类型

2.转换函数是特殊的类成员运算符函数,用于将类对象转换为其他对象,转换函数是类的成员,没有参数,没有返回类型,该函数能自动被调用

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

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

相关文章

C++---vector容器

是STL容器中的一种常用的容器&#xff0c;由于其大小(size)可变&#xff0c;常用于数组大小不可知的情况下来替代数组。vector容器与数组十分相似&#xff0c;被称为动态数组。时间复杂度为O&#xff08;1&#xff09;。 数组数据通常存储在栈中&#xff0c;vector数据通常存储…

napi系列学习进阶篇——NAPI生命周期

什么是NAPI的生命周期 我们都知道&#xff0c;程序的生命周期是指程序从启动&#xff0c;运行到最后的结束的整个过程。生命周期的管理自然是指控制程序的启动&#xff0c;调用以及结束的方法。 而NAPI中的生命周期又是怎样的呢&#xff1f;如下图所示&#xff1a; 从图上我们…

16、普通数组-除自身以外的数组乘积

思路 通过辅助数组的方式 第一个从左向右的辅助数组乘积第二次从右向左的辅助数组乘积对于0<i<N-1 他的数组乘积就是左边的数组乘积*右边数组乘积然后再分类讨论i0 就是右边1-N-1的数组乘积iN-1就是左边从N-2到0的数组乘积 代码如下&#xff1a; class Solution {pub…

开发日志2024-04-12

开发日志2024/04/12 1、分店月业绩和年业绩都需要添加为真实数据 **开发思路&#xff1a;**分店下所属的技师的业绩总和 代码实现&#xff1a; 前端 无 后端 //TODO 将技师多对应的积分累加到他所属的分店的月/年累计业绩销量中//TODO 查询技师所对应的分店地址String f…

【深度学习】图像超分辨

案例 7&#xff1a;图像超分辨 相关知识点&#xff1a;生成对抗网络、图像处理&#xff08;PIL&#xff09;和可视化&#xff08;matplotlib&#xff09; 1 任务目标 1.1 任务和数据简介 ​ 本次案例将使用生成对抗网络来实现 4 倍图像超分辨任务&#xff0c;输入一张低分辨…

中级物流师、高级物流师资格认证考试大纲《物流管理实务》

物流管理实务 第一章 物流市场调查 一、市场调查基本知识 二、物流市场调研 三、物流市场预测 四、物流市场调研报告 第二章 仓库规划与设计 一、仓储规划概述 二、仓库规模和数量规划 三、仓库选址规划 四、仓库的结构与布局 五、自动化立体仓库的规划与设计 第…

微信小程序报错——“errno“: 600001, “errMsg“: “request:fail -2:net::ERR_FAILED“

bug现象 微信小程序体验版和真机调试 进入小程序的时候接口就出现了这个报错 "errno": 600001, "errMsg": "request:fail -2:net::ERR_FAILED" 排查 检查是证书过期还是证书链不完整 证书的信任链完整问题&#xff0c;可以在 亚数信息-SSL/TLS安…

安装ODBC方法

1、运行 搜索 ODBC数据源管理程序 32位或者 64位 2、在用户DSN或者系统DSN选择添加&#xff08;建议前者&#xff09;&#xff0c;此处以添加access数据库的odbc驱动为例 3、安装成功

Unity让地图素材遮挡人物

点击编辑/项目设置/图形&#xff0c;透明度排序模式设置自定义轴&#xff0c;透明度排序轴Y设置为1其他为0。 此时人物和地图素材的图层排序相等&#xff0c;当人物的高度大于地图素材时&#xff0c;人物则被遮挡。

锚框(anchor box)

目标检测算法通常会在输入图像中抽样大量的区域&#xff0c;然后判断这些区域中是否包含我们感兴趣的目标&#xff0c;并调整区域边界&#xff0c;从而更准确地预测目标的真实边界框&#xff08;ground-truth bounding box)。不同的模型所使用的区域抽样方法可能不同。这里我们…

三小时零基础入门微信扫码点餐小程序 手把手带你开发一款云开发版点餐软件,店铺地图导航,外卖小程序,用户端和后厨端都有

从今天开始带领大家实现一款云开发版的点餐小程序 视频讲解&#xff1a;《云开发后台微信扫码点餐小程序cms网页管理后台》 技术选型 1&#xff0c;前端 微信小程序原生框架cssJavaScript 2&#xff0c;管理后台 云开发Cms内容管理系统web网页 3&#xff0c;数据后台 小…

【STL】list

目录 1. list的使用 1.1 list的构造 1.2 list iterator的使用 1.3 list capacity 1.4 list element access 1.5 list modifiers 1.6 list的迭代器失效 2. list的模拟实现 3. list与vector的对比 1. list的使用 1.1 list的构造 1.2 list iterator的使用 1. begin与end为…

MySQL前缀索引(3/16)

前缀索引 前缀索引&#xff1a;MySQL支持前缀索引&#xff0c;允许定义字符串的一部分作为索引。如果不指定前缀长度&#xff0c;索引将包含整个字符串。前缀索引可以节省空间&#xff0c;但可能会增加查询时的记录扫描次数&#xff08;因为会查询到多个前缀相同的数据&#x…

4.9QT

完善对话框&#xff0c;点击登录对话框&#xff0c;如果账号和密码匹配&#xff0c;则弹出信息对话框&#xff0c;给出提示”登录成功“&#xff0c;提供一个Ok按钮&#xff0c;用户点击Ok后&#xff0c;关闭登录界面&#xff0c;跳转到其他界面 如果账号和密码不匹配&#xf…

Redis数据持久化 AOF RDB

Redis数据持久化 AOF RDB 1、单点 redis 的问题2、主从复制2.1 命令传播 3、Redis的持久化3.1 AOF写回策略重写机制后台重写 3.2 RDB&#xff08;默认方式&#xff09;RDB 方式&#xff1a;执行快照时&#xff0c;数据能被修改吗&#xff1f;RDB 方式总结 3.3 RDB 和 AOF 组合&…

【springboot开发】MVC和SSM

前言&#xff1a;关于MVC和SSM基本内容的梳理&#xff0c;以及两者之间的关系。 文章目录 1. 三层架构2. MVC3. SSM 1. 三层架构 三层架构是指&#xff1a; 视图层view&#xff08;表现层&#xff09;: 用于显示数据和接收用户输入的数据&#xff0c;为用户提供一种交互式操作…

684. 冗余连接

684. 冗余连接 原题链接&#xff1a;完成情况&#xff1a;解题思路&#xff1a;参考代码&#xff1a;错误经验吸取 原题链接&#xff1a; 684. 冗余连接 https://leetcode.cn/problems/redundant-connection/description/ 完成情况&#xff1a; 解题思路&#xff1a; 这段…

[react优化] 避免组件或数据多次渲染/计算

代码如下 点击视图x➕1,导致视图更新, 视图更细导致a也重新大量计算!!这很浪费时间 function App() {const [x, setX] useState(3)const y x 2console.log(重新渲染, x, y);console.time(timer)let a 0for (let index 0; index < 1000000000; index) {a}console.timeE…

论文阅读AI工具链

文献检索 可以利用智谱清言来生成合适的文献检索式&#xff0c;并根据需要不断调整。 谷歌学术 在Google Scholar中进行检索时&#xff0c;您可以使用类似的逻辑来构建您的搜索式&#xff0c;但是语法会有所不同。Google Scholar的搜索框接受普通的文本搜索&#xff0c;但是…

面试-数据库基础以及MySql、ClickHost、Redis简介

面试-数据库基础以及MySql、ClickHost、Redis简介 0.数据完整性1.数据库并发控制1.1事物1.2 并发读写错误1.3 锁1.3.1 乐观锁与悲观锁1.3.2 共享锁和排他锁1.3.3 行锁与表锁1.3.4 意向锁 1.4 封锁协议与隔离级别1.5 MVCC1.5.1 概念1.5.2 当前读与快照读1.5.3 MVCC in InnoDB 2.…