C++重载的奥义之运算符重载

0、引言

        重载,顾名思义从字面上理解就是重复装载,打一个不恰当的比方,你可以用一个篮子装蔬菜,也可以装水果或者其它,使用的是同一个篮子,但是可以用篮子重复装载的东西不一样。

        正如在之前的文章《重载的奥义之函数重载》中介绍的类似,函数的重载是指利用相同的函数名设计一系列功能相近,但是功能细节不一样的函数接口;因此运算符重载也是指对于同一个运算符来说,它可以用于实现不同的功能。下面就一起来理解下运算符重载的应用。

1、运算符重载定义

        正常来说,我们一般使用的运算符是对基本的数据类型进行操作,但是在C++中有了对象,导致对象无法通过运算符进行运算,故引入了运算符重载即需要重新的定义这些运算符,赋予已有运算符新的功能,使它能够用于特定类型执行特定的操作。运算符重载的实质是函数重载,它提供了C++的可扩展性。

        运算符重载是通过创建运算符函数实现的,运算符函数定义了重载的运算符将要进行的操作。运算符函数的定义与其他函数的定义类似,唯一的区别是运算符函数的函数名是由关键字operator和其后要重载的运算符符号构成的。运算符函数定义的一般格式如下:

1 <返回类型说明符> operator <运算符符号>(<参数表>)
2 {
3      <函数体>
4 }

C++重载的奥义之运算符重载

        其中,“返回类型说明符”指出重载运算符的返回值类型,operator是定义运算符重载函数的关键字,“运算符符号”指出要重载的运算符名字,是C++中可重载的运算符,比如要重载加法运算符,这里直接写“+”即可,“参数表”指出重载运算符所需要的参数及其类型。可以看出,运算符重载是一种形式C++多态的体现。

        例如,使用“+”将两个对象相加,编译器将根据操作数的数目和类型决定使用哪种加法定义,这样可以让代码看起来更加自然。

 1 //正常情况下两个数组的数相加
 2 for(int i= 0; i<10; i++)
 3     c[i] = a[i] + b[i];
 4 //可以通过定义一个数组的类,重载“+”运算符后
 5 //隐藏了内部机制,并强调了实质
 6 arry operator+(arry p,arry q)
 7 {
 8    arry t;
 9     for(int i= 0; i<10; i++)  //c = a + b; 
10     {  
11       t.a[i]=p.a[i]+q.a[i];
12     }
13     return t;
14 }

C++重载的奥义之运算符重载

        运算符重载就是对已有的运算符重新进行定义,赋予其另一种功能,以达到适应不同的数据类型。运算符重载不能改变它本来的寓意(也就是加法不能变更为减法),运算符重载只是一种 “语法上的方便” ,它只是一种函数调用的方式。

2、作为成员函数进行重载

        我们就以“+”运算符重载举例:

 1 #include <iostream>
 2 using namespace std;
 3 class addfloat
 4 {
 5 public:
 6     addfloat(float p);
 7     //声明运算符重载
 8     addfloat operator+(const addfloat &A) const;
 9     void show() const;
10 private:
11     float m_p;  
12 };
13 addfloat::addfloat(float p)
14 {
15     m_p = p;
16 }
17 //作为类的成员函数实现运算符重载
18 addfloat addfloat::operator+(const addfloat &A) const
19 {
20     addfloat B;
21     B.m_p = this->m_p + A.m_p;
22     return B;
23 }
24 void addfloat::show() const
25 {
26     cout<<"输出结果是"<<m_p<<endl;
27 }
28 
29 
30 int main()
31 {
32     addfloat m(5.1);
33     addfloat n(1.5);
34     addfloat t;
35     t = m + n; //两个addfloat类对象相加:t = m.operator+(n);
36     t.show();
37     return 0;
38 }

C++重载的奥义之运算符重载

        运行结果为:

1 输出结果是6.6

        从上面的例子可以看出,在addfloat类中对“+”运算符进行了重载 ,重载后可以对该类的对象进行加法运算。当运行 t = m + n时,编译器检测到“+”左边的m(“+”具有左结合性,所以先检测左边)是一个 addfloat类对象,就会调用成员函数 operator+(),将表达式转换成如下格式:

        t = m.operator + (n);

        表达式中m作为调用函数的对象,n作为函数的实参。

3、作为全局函数进行重载

        对于之前的例子:t = m + n,m和n是作为addfloat类的对象进行相加的,使用成员函数 operator+()转换为了t = m.operator+(n),如果n不是类的对象,而是一个常数,例如:

        t = m + 5.2;那么可以转换t = m.operator+(5.2);

        但是如果m是一个常数时,即:t = 5.2 + n;则t = (5.2).operator + (n)这种转换是不允许的,编译器会报错,因为5.2不能作为类的对象调用运算符重载operator+()函数。

        这种场景下针对“+”这种运算符作为类的成员函数进行重载是不可以的。运算符重载不仅仅可以通过类的成员函数来实现,也可以通过全局函数来实现。

        我们需要将运算符重载的全局函数声明为友元函数,因为大多数时候重载运算符要访问类的私有数据,(当然也可以设置为非友元非类的成员函数,但是非友元又不是类的成员函数,是没有办法直接访问类的私有数据的),如果不声明为类的友元函数,而是通过在此函数中调用类的公有函数来访问私有数据会降低性能。所以一般都会设置为类的友元函数,这样我们就可以在此非成员函数中访问类中的数据了。

 1 #include <iostream>
 2 using namespace std;
 3 class addfloat
 4 {
 5 public:
 6     addfloat(float p);
 7     //声明为友元函数
 8     friend addfloat operator+(const addfloat &A, const addfloat &B);
 9     void show() const;
10 private:
11     float m_p;  
12 };
13 addfloat::addfloat(float p)
14 {
15     m_p = p;
16 }
17 
18 void addfloat::show() const
19 {
20     cout<<"输出结果是"<<m_p<<endl;
21 }
22 
23 //作为全局函数进行重载
24 addfloat operator+(const addfloat &A, const addfloat &B)
25 {
26     addfloat C;
27     C.m_p = A.m_p + B.m_p;
28     return C;
29 }
30 
31 int main()
32 {
33     addfloat m(5.1);
34     addfloat n(1.5);
35     addfloat t;
36     t = m + n; //两个addfloat类对象相加:t = m.operator+(n);
37     t.show();
38     return 0;
39 }

        由上述程序可以看出,运算符重载函数operator+()不是 addfloat类的成员函数,但是却用到了 addfloat类的 private 成员变量m_p,所以需要在 addfloat类中将operator+()函数声明为友元函数。

        当运行t = m + n时,编译器检测到“+”两边都是addfloat类的对象,就会转换为类似下面的函数调用:

        t = operator + (m, n);

        因此,m和n都可以看作是函数的实参:

        t = m + 5.2转换为 t = operator + (m, 5.2);

        t = 5.2 + n转换为 t = operator + (5.2, n);

        以全局函数的形式重载“+”,是为了保证“+”运算符的操作数能够被对称的处理;换句话说,常数在“+”左边和右边都是正确的;

        因此,运算符左右两边都有操作对象时,且这两个操作对象可以互换,最好可以使用全局函数的形式重载,例如:+、-、*、/、==、!= ,这些符合运算符两边有操作对象的运算符。

4、运算符重载的一些规则

(1)可以重载的运算符

C++重载的奥义之运算符重载

(2)不可以重载的运算符

.         (成员访问运算符)

.*       (成员指针访问运算符)

::        (域运算符)

sizeof (长度运算符)

?:        (条件运算符)

(3) 只能以成员函数的形式重载的运算符(与 this关联太多)

=         (赋值运算符)

()         (函数调用运算符)

[]         (下标运算符)

->       (成员访问运算符)  

(4)只能以全局函数重载的运算符

<<      (左移运算符)  

>>      (右移运算符)  

(5)运算符重载函数既可以作为类的成员函数,也可以作为全局函数。友元函数运算符重载函数与成员运算符重载函数的区别是:友元函数没有this指针,而成员函数有,因此,在两个操作数的重载中友元函数有两个参数,而成员函数只有一个。

(6)有一部分运算符重载既可以是成员函数也可以是全局函数,虽然没有一个必然的、不可抗拒的理由选择成员函数,但我们应该优先考虑成员函数,这样更符合运算符重载的初衷。

(7)对于复合的赋值运算符如 +=、-=、*=、/=、&=、!=、~=、%=、>>=、<<= 建议重载为成员函数;

单目运算符最好重载为成员函数;

对于其它运算符,建议重载为全局函数。

(8)使用运算符不能违反运算符原来的语法规则,原来有几个操作数、操作数在左边还是在右边,这些都不会改变。算符重载函数不能有默认的参数,否则就改变了运算符操作数的个数。

(9)运算符的优先级不能被重载改变。然而,圆括号能够强制改变表达式中重载运算符的求值顺序。

(10)运算符的结合性不能被重载改变。如果一个运算符的结合性是从左向右,那么,它的所有重载的版本的结合性依然是从左向右

(11)不能创造新的运算符,即只能重载现有的运算符。例如不能定义operator** (···)来表示求幂。

(12)重载的运算符必须和用户定义的对象一起使用,运算符参数(操作的对象)中至少应有一个是类对象(或类对象的引用)。


↓↓↓更多技术内容和书籍资料获取,入群技术交流敬请关注“明解嵌入式”↓↓↓

C++重载的奥义之运算符重载

原文链接:https://www.cnblogs.com/Sharemaker/p/17327752.html

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C++重载的奥义之运算符重载 - Python技术站

(0)
上一篇 2023年4月17日
下一篇 2023年4月18日

相关文章

  • STL容器之queue

    是什么 循环队列, FIFO先进先出 怎么用 初始化 //C11 deque<int> deq{1,2,3,4,5}; //拷贝构造,可以拷贝deque queue<int> que(deq); //100个5 queue<int> que2(100,5); //运算符重载 que2 = que; 操作 //队尾添加元素 …

    C++ 2023年4月17日
    00
  • 驱动开发:内核使用IO/DPC定时器

    本章将继续探索驱动开发中的基础部分,定时器在内核中同样很常用,在内核中定时器可以使用两种,即IO定时器,以及DPC定时器,一般来说IO定时器是DDK中提供的一种,该定时器可以为间隔为N秒做定时,但如果要实现毫秒级别间隔,微秒级别间隔,就需要用到DPC定时器,如果是秒级定时其两者基本上无任何差异,本章将简单介绍IO/DPC这两种定时器的使用技巧。 首先来看IO…

    C++ 2023年4月18日
    00
  • 【Visual Leak Detector】源码文件概览

    说明 使用 VLD 内存泄漏检测工具辅助开发时整理的学习笔记。本篇对 VLD 源码包中的各文件用途做个概述。同系列文章目录可见 《内存泄漏检测工具》目录 目录 说明 1. 整体概览 2. 文件夹 .teamcity 3 文件夹 lib 3.1 文件夹 cppformat(生成 libformat) 3.2 文件夹 dbghelp 3.3 文件夹 gtest(…

    C++ 2023年4月24日
    00
  • 玩一玩 Ubuntu 下的 VSCode 编程

    一:背景 1. 讲故事 今天是五一的最后一天,想着长期都在 Windows 平台上做开发,准备今天换到 Ubuntu 系统上体验下,主要是想学习下 AT&T 风格的汇编,这里 Visual Studio 肯定是装不了了,还得上 VSCode,刚好前几天买了一个小工控机,这里简单记录下 零到一 的过程吧。 二:搭建一览 1. VSCode 安装 在 U…

    C++ 2023年5月3日
    00
  • 【Visual Leak Detector】源码编译 VLD 库

    说明 使用 VLD 内存泄漏检测工具辅助开发时整理的学习笔记。本篇介绍 VLD 源码的编译。同系列文章目录可见 《内存泄漏检测工具》目录 目录 说明 1. VLD 库的依赖文件 2. 源码编译生成 VLD 库 3. 配置环境变量 4. 使用 VLD 库 1. VLD 库的依赖文件 以 vld2.5.1 版本为例,下载源码 后,源码包中各文件的用途可看本人另一…

    C++ 2023年4月24日
    00
  • C++动态分配(new)二维数组的若干方法

    写在前面 之前刷动态规划的题目,多需要用到二维数组(也许后面再优化成一维)。如果每次都按照给定数的范围直接声明为全局二维数组变量,又总觉得的不够优雅。查阅了一些网上的资料后,总结了一些使用方法,就写下这篇博文用以记录。 方法1——动态分配(new)一维数组,再强制类型转换为二维(个人使用,推荐指数:⭐⭐⭐⭐) 直接看例子 /** 假设需要根据两个string…

    C++ 2023年4月17日
    00
  • C++/Qt网络通讯模块设计与实现(六)

    前面章节主要讲述网络通讯客户端的实现,各位小伙伴需认真阅读以及理解,理会其中的思想,有疑问的地方可及时给我私信,我都会非常认真地解答大家的疑惑。 C++/Qt网络通讯模块设计与实现(一) C++/Qt网络通讯模块设计与实现(二) C++/Qt网络通讯模块设计与实现(三) C++/Qt网络通讯模块设计与实现(四) C++/Qt网络通讯模块设计与实现(五) 这节…

    C++ 2023年4月18日
    00
  • 【Visual Leak Detector】配置项 StartDisabled

    说明 使用 VLD 内存泄漏检测工具辅助开发时整理的学习笔记。本篇介绍 VLD 配置文件中配置项 StartDisabled 的使用方法。同系列文章目录可见 《内存泄漏检测工具》目录 目录 说明 1. 配置文件使用说明 2. 设置是否禁用自动初始化 2.1 测试代码 2.2 StartDisabled = no 时的输出 2.3 StartDisabled …

    C++ 2023年4月18日
    00
合作推广
合作推广
分享本页
返回顶部