C++学习笔记----8、掌握类与对象(六)---- 操作符重载(1)

ops/2024/10/9 4:04:19/

        经常在对象上执行如相加,比较,文件传输等操作。例如,spreadsheet只有在可以在上面执行自述运算才有用,比如对整行的单元格求和。所有这些都可以通过重载操作符来完成。

        许多人发现操作符重载的语法复杂而令人迷惑。至少一开始是这样。真实情况是想让事情更简单。在本节你会发现,那并不意味着写类时更简单,只是使用类时更简单。关键点是让新类与内建像int与double这样的类型一样简单:使用+来使对象相加要比记住不管是add()或是sum()这样的成员函数名要容易多了。

        注意:作为服务给客户提供类的操作符重载。

        在这一点上,你可能会想到底哪些操作符可以重载呢?答案是几乎所有--甚至是你没有听说过的。本章会浮光掠影地简单涉及一下:赋值操作符在本章的前面 已经解释过了,而本节要介绍的是基础的算术操作符,缩写的算术操作符,以及比较操作符。重载流插入与释放操作符也比较有用。还有,有一些麻烦但是又比较有趣的东东,一开始你可能并不想用操作符重载来做。在标准库中大量使用操作符重载。以后我们会解释怎么以及什么时候重载其他的操作符。以及标准库方面的内容。

1、例子:SpreadsheetCell的另外内容的实现

        在真正的面向对象的编程风格中,SpreadsheetCell对象应该能够把自己与其它SpreadsheetCell对象相加。将一个单元格与另一个单元格相加生成结果为第三个单元格。这并不会改变原来的任一单元格。SpreadsheetCell相加的意思就是单元格值的相加。

1.1、第一次尝试:加成员函数

        可以为SpreadsheetCell类像这样声明与定义add()成员函数:

export class SpreadsheetCell
{
public:SpreadsheetCell add(const SpreadsheetCell& cell) const;// Omitted for brevity
};

        该成员函数将两个单元格相加,返回一个新的第三个单元格 ,其值是前两个的和。声明为const并且 用一个const SpreadsheetCell的引用是因为add()不会改变任意一个源单元格。下面是其实现:

SpreadsheetCell SpreadsheetCell::add(const SpreadsheetCell& cell) const
{return SpreadsheetCell { getValue() + cell.getValue() };
}

        可以像这样来使用add()成员函数:

SpreadsheetCell myCell { 4 }, anotherCell { 5 };
SpreadsheetCell aThirdCell { myCell.add(anotherCell) };
auto aFourthCell { aThirdCell.add(anotherCell) };

        这也可以达到目的,但是有一点儿繁琐,我们可以做得更好。

1.2、第二次尝试:重载操作符+作为成员函数

        用加号类似于两个int或者两个double的方式来将两个单元格相加会比较方便--有点儿像下面这样:

SpreadsheetCell myCell { 4 }, anotherCell { 5 };
SpreadsheetCell aThirdCell { myCell + anotherCell };
auto aFourthCell { aThirdCell + anotherCell };

        c++允许你写自己的加号版本,叫做加操作符,来正确地作用于类。为了做到这一点,写一个名字叫做operator+的成员函数,看起来像这样:

export class SpreadsheetCell
{
public:SpreadsheetCell operator+(const SpreadsheetCell& cell) const;// Omitted for brevity
};

        注意:在operator与加号之间可以加入空格。例如,可以将operator+写成operator +。我们还是使用没有空格的风格。

        重载的operator+成员函数的定义与add()成员函数的实现是一样的:

SpreadsheetCell SpreadsheetCell::operator+(const SpreadsheetCell& cell) const
{return SpreadsheetCell { getValue() + cell.getValue() };
}

        现在你就可以使用前面展示的加号操作符来将两个单元格 相加了。

        我们还是比较习惯于这种语法。不要过于担心奇怪的成员函数名operator+----它就是一个类似于foo或add的名字而已。为了理解语法的其余部分,它有助于理解真实发生的情况。当c++编译器解析程序时,遇到比如说+,-,=,或<<这样的操作符,它会用operator+,operator-,operator=,或operator<<这样的名字来找函数或成员函数,特别是,带有适合的参数的。例如,当编译器看到下面的代码行,它会尝试在SpreadsheetCell类中去找成员函数名字为operator+,接受另一个SpreadsheetCell作为参数(或者,在本章后面会讨论到的,接受两个SpreadsheetCell参数的全局函数,名字叫做operator+):

SpreadsheetCell aThirdCell { myCell + anotherCell };

        如果SpreadsheetCell类包含这样一个operator+成员函数,上面的行就会被翻译成这样:

SpreadsheetCell aThirdCell { myCell.operator+(anotherCell) };

        注意,operator+没有要求其参数的对象是与它所写的类是同一种类型。你可以写一个SpreadsheetCell的operator+,拿一个Spreadsheet去加到SpreadsheetCell。这对于程序员是讲不通的,但是编译器是允许的。下一节会给出一个SpreadsheetCell的operator+接受double值的例子。

        也要注意,可以让operator+返回你想的任何类型。然而,应该遵循最少惊奇的原则;也就是说,operator+的返回类型通常应该是用户想要的。

1.2.1、隐式转换

        令人吃惊的是,一旦写完前面所示的operator+,不但可以将两个单元格相加,也可以将一个单元格加到一个string_view,一个double,或一个int!下面是一些例子:

SpreadsheetCell myCell { 4 }, aThirdCell;
string str { "hello" };
aThirdCell = myCell + string_view{ str };
aThirdCell = myCell + 5.6;
aThirdCell = myCell + 4;

        这些代码能跑的原因是编译器会比尝试找到合适的operator+的所指的正确的类型更多的工作。编译器也会尝试找到合适的类型转换。SpreadsheetCell类有转换构造函数将double或string_view转换成一个SpreadsheetCell。在前面的例子中,当编译器看到一个SpreadsheetCell尝试将自身加到一个double上,它发现SpreadsheetCell构造函数用了double并且 构建了一个临时的SpreadsheetCell对象传递给operator+。同样的,当编译器看到代码行尝试将一个SpreadsheetCell加到一个string_view上时,它会调用string_view SpreadsheetCell构造函数生成一个临时的SpreadsheetCell传递给operator+。

        要记住,虽然使用隐式转换构造函数可以效率不高,因为要生成临时对象。在这个例子中,为了避免隐式构建与double相加,可以写如下的第二个operator+:

SpreadsheetCell SpreadsheetCell::operator+(double rhs) const
{return SpreadsheetCell { getValue() + rhs };
}

http://www.ppmy.cn/ops/122974.html

相关文章

Dev-C++ 安装与使用(dev c++官网)(已解决)

1.Dev-C的安装 ①打开Dev-C的官网(https://sourceforge.net/projects/orwelldevcpp/ )&#xff1b;点击Download(下载)&#xff0c;等待5秒后开始下载。 ②点开下载好的EXE文件&#xff0c;等待加载完成(如图)。 右键&#xff0c;以管理员身份 运行安装包。 选择English(英语),…

银河麒麟,apt 安装软件报错640Unknown Status

今天把银行麒麟的机器恢复出厂了&#xff0c;然后apt install 安装极其不稳定&#xff0c;故障现象如下图所示&#xff1a; 错误提示里面有&#xff1a; 640 Unknown Status [IP: 106.116.184.122 80] E: 无法下载 http://archive.kylinos.cn/kylin/KYLIN-ALL/pool/universe/f…

论文速读:基于渐进式转移的无监督域自适应舰船检测

这篇文章的标题是《Unsupervised Domain Adaptation Based on Progressive Transfer for Ship Detection: From Optical to SAR Images》基于渐进式转移的无监督域自适应舰船检测:从光学图像到SAR图像&#xff0c;作者是Yu Shi等人。文章发表在IEEE Transactions on Geoscience…

【Android】获取备案所需的公钥以及签名MD5值

目录 重要前提 获取签名MD5值 获取公钥 重要前提 生成jks文件以及gradle配置应用该文件。具体步骤请参考我这篇文章&#xff1a;【Android】配置Gradle打包apk的环境_generate signed bundle or apk-CSDN博客 你只需要从头看到该文章的配置build.gradle&#xff08;app&…

【答疑解惑】图文深入详解undo和redo的区别及其底层逻辑

题记&#xff1a;最近有些人问我&#xff0c;undo和redo到底是什么关系&#xff0c;他们中不乏已经入行3-4年的同学&#xff0c;今天咱们就来深入探讨下到底什么是undo和redo&#xff0c;他们分别做什么&#xff0c;底层逻辑原理是什么等等。 1. undo 1.1 undo的存储结构 Un…

游戏中的对象池技术探索(一)

前言 对象池技术在游戏开发中的应用非常普遍&#xff0c;它是一种高效管理对象实例的技术&#xff0c;能够避免频繁和重复创建对象所带来的性能开销。本篇文章我们就来探索一下如何在游戏开发中设计通用对象池&#xff0c;使之易于使用和扩展。 代码 代码目录结构 ObjectPool …

Pikachu-unsafe upfileupload-getimagesize

什么是getimagesize()&#xff1f; getimagesize()是PHP中用于获取图像的大小和格式的函数。它可以返回一个包含图像的宽度、高度、类型和MIME类型的数组。 由于返回的这个类型可以被伪造&#xff0c;如果用这个函数来获取图片类型&#xff0c;从而判断是否时图片的话&#xff…

Pigar:Python 项目的依赖管理利器

&#x1f31f; 引言 在Python项目开发过程中&#xff0c;依赖管理是一个不可忽视的环节。一个精确且易于维护的requirements.txt文件对于项目的部署和协作至关重要。今天&#xff0c;我们将介绍一款名为Pigar的自动生成requirements.txt文件的依赖管理工具&#xff0c;它通过一…