目录

前言

正文

1. 不能重载的运算符

2.怎么写运算符重载

3.一元运算符和二元运算符:

4.思考一个问题

总结

前言

https://blog.csdn.net/qq_42683011/article/details/102087764

基础应用:
重载运算符最需要考虑的即为参数与返回值问题

(这里以operator = 为例):

参数:
        一般地,赋值运算符重载函数的参数是函数所在类的const类型的引用, 如:

MyStr& operator =(const MyStr& str);
加const是因为:

我们不希望在这个函数中对用来进行赋值的“原版”做任何修改。
        加上const,对于const的和非const的实参,函数都能接受;如果不加,就只能接受非const的实参。
用引用是因为:

这样可以避免在函数调用时对实参的一次拷贝,提高了效率
注意:
        上面的规定都只是推荐,可以不加const,也可以没有引用,甚至参数可以不是函数所在的对象。

返回值:
        一般地,返回值是被赋值者的引用(但有时返回左值还是右值需要相当的考虑),即*this

MyStr& operator =(const MyStr& str);
        这样在函数返回时避免一次拷贝,提高了效率。

更重要的,根据赋值运算符的从左向右的结合律, 可以实现连续赋值,即类似a=b=c
        如果返回的是值,则执行连续赋值运算后后头得到的将是一个匿名副本, 为不可更改的右值,就是说是const类型, 再执行=c就会出错。

注意:
        这也不是强制的,完全可以将函数返回值声明为void,然后什么也不返回,只不过这样就无法连续赋值,所以具体的返回值与参数的设定完全取决于需求, 是非常值得设计者考量的。

正文

1. 不能重载的运算符

C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。函数名字为:关键字operator后面接需要重载的运算符符号。函数原型:返回值类型 operator操作符(参数列表)

注意:
1). 不能通过连接其他符号来创建新的操作符:比如operator@
2). 重载操作符必须有一个类类型参数
3). 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不能改变其含义
4). 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this
5).注意以下5个运算符不能重载。

运算符重载是具有特殊函数名的函数
函数名:operator运算符 //eg:operator==
有几个操作数,就有几个参数
参数:运算符操作数
返回值:运算符运算后的结果

2.怎么写运算符重载

 下面简单写一下==的运算符重载:

#include<iostream>
using namespace std;
class Data
{
public:Data(int year = 1, int month = 1, int day = 1)//自己写的全缺省构造{_year = year;_month = month;_day = day;}Data(const Data& d)//拷贝构造{_year = d._year;_month = d._month;_day = d._day;}//private:int _year;int _month;int _day;
};
bool operator==(Data d1, Data d2)//运算符重载函数
{return d1._year == d2._year&& d1._month == d2._month&& d1._day == d2._day;
}
int main()
{Data d1(2022, 10, 13);Data d2(2022, 10, 13);if (operator==(d1, d2)){cout << "==" << endl;}if (d1 == d2)
等价于if(operator==(d1, d2)),编译器自动处理成对应的运算符重载{cout << "==" << endl;}return 0;
}

发现如果使用运算符重载成全局的就需要成员变量是公有的,那么问题来了,封装性如何保证?且这不就相当于额外写了一个函数,如果两个数相等就返回1,不相等就返回0。

那如果修改成以下代码?发现这样会报错。

#include<iostream>
using namespace std;
class Data
{
public:Data(int year = 1, int month = 1, int day = 1)//自己写的全缺省构造{_year = year;_month = month;_day = day;}Data(const Data& d)//拷贝构造{_year = d._year;_month = d._month;_day = d._day;}bool operator==(Data d1, Data d2)//运算符重载函数{return d1._year == d2._year&& d1._month == d2._month&& d1._day == d2._day;}
private:int _year;int _month;int _day;
};int main()
{Data d1(2022, 10, 13);Data d2(2022, 10, 13);if (operator==(d1, d2)){cout << "==" << endl;}if (d1 == d2){cout << "==" << endl;}return 0;
}

这样写会报错,==的参数太多了,是因为成员函数还有一个隐含的指针this。

所以需要进行修改成如下代码:

        作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this。d1默认传给了this,this->_month == d._month,让this和d进行比较。

//bool operator==(Data d1, Data d2)

//if (operator==(d1, d2))

bool operator==(Data d)
运算符重载是具有特殊函数名的函数,编译器其实会处理成(Data* const this,Data d){return _year == d._year//d1默认传给了this,&& _month == d._month//this->_month = d._month//让this和d进行比较&& _day == d._day;}

d1默认传给了this,operator==(Data d)这个整体看成一个成员函数,而调用成员函数就是形式是(d1.函数名),然后对于其参数Data d,传参传入d2进行比较

int main()
{Data d1(2022,10,13);Data d2(2022, 10, 13);//作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的thisif (d1.operator==(d2))//operator==(Data d){cout << "==" << endl;}if (d1 == d2){cout << "==" << endl;}return 0;
}

对于以上代码还可以进行优化:

 因为传值传参会调用拷贝构造,所以最好加上引用,为了防止写反,最好加上const

#include<iostream>
using namespace std;
class Data
{
public:Data(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}Data(const Data& d){_year = d._year;_month = d._month;_day = d._day;}bool operator==(const Data& d)//这样形参传给实参就不用调用拷贝构造函数了,因为只有自定义类型传值传参就会调用拷贝构造函数{return _year == d._year&& _month == d._month&& _day == d._day;}
private:int _year;int _month;int _day;
};
int main()
{Data d1(2022, 10, 13);Data d2(2022, 10, 13);if (d1 == d2){cout << "==" << endl;}return 0;
}

3. 一元运算符和二元运算符:

在重载一个运算符为成员函数时,其参数表中没有任何参数,这说明该运算符是 ( )A.无操作数的运算符B.二元运算符C.前缀一元运算符D.后缀一元运算符

A.重载为成员函数时,其函数的参数个数与真实的函数参数个数会减少1个,减少的则通过this指针进行传递,所以无参 则说明有一个参数,故错误

B.无参成员函数相当于有一个参数的全局函数,不能是二元运算符

C.正确

D.区分前缀后缀时,后缀运算需要加一个int参数

 前置和后置因为都是一样的,所以用int i的参数来区分,(注意i可以不写,形参不写代表着不用这个值或者说不接收)直接写int也可以 规定无参为前置++ ,这里只是改变了函数名的修饰规则以用来区分。

Data operator++(int i = 0);这样全缺省是错误的,因为i没有实际的意义,只是用来区分前置与后置++,或者说是全缺省和无参的函数在调用时区分不开。

4.思考一个问题 

        从下代码发现:当bool operator==(const Data& d1, const Data& d2)和bool operator==(const Data& d)同时存在时可以编译通过,为什么可以同时存在?

#include<iostream>
using namespace std;
class Data
{
public:Data(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}Data(const Data& d){_year = d._year;_month = d._month;_day = d._day;}bool operator==(const Data& d)
//这样形参传给实参就不用调用拷贝构造函数了,因为主要自定义类型传值传参就会调用拷贝构造函数{return _year == d._year&& _month == d._month&& _day == d._day;}
//private:int _year;int _month;int _day;
};
bool operator==(const Data& d1, const Data& d2)
{return d1._year == d2._year&& d1._month == d2._month&& d1._day == d2._day;
}
int main()
{Data d1(2022, 10, 13);Data d2(2022, 10, 13);if (d1 == d2){cout << "==" << endl;}return 0;
}

因为一个是全局函数,一个是类里面的函数,他们的函数名相同,没有关系。但是他会优先调用类里面的重载。可以通过上面代码的调试发现:


类成员函数和全局函数的对比:
        1).类成员函数和全局函数的区别就是,一个是面向对象,一个是面向过程,这同样也是c与c++的区别;
        2).类成员函数==(转成)==>全局函数:增加一个参数,增加的这个参数是代替this指针的;
        3).全局函数==(转成)==>类成员函数:减少一个参数,减少的这个参数通过this指针隐藏。

5. 那么写好了==,>、<、=怎么写了?

        以下以<为例子:bool operator<(const Data& d)

#include<iostream>
using namespace std;
class Data
{
public:Data(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}Data(const Data& d){_year = d._year;_month = d._month;_day = d._day;}bool operator<(const Data& d)
//这样形参传给实参就不用调用拷贝构造函数了,因为主要自定义类型传值传参就会调用拷贝构造函数{if ((_year < d._year)|| (_year == d._year && _month < d._month)|| (_year == d._year && _month == d._month && _day < d._day)){return true;}else{return false;}}
private:int _year;int _month;int _day;
};
int main()
{Data d1(2022, 9, 14);Data d2(2022, 10, 14);if (d1 < d2){cout << "<" << endl;}return 0;
}

 注意:=怎么写?依葫芦画瓢如下:

        但是这样写其实不对,像赋值语句j = i,返回值其实是j,但是这样返回值是void,如果需要满足连续赋值k = j = i,就需要对返回值进行修改。

 //d2 = d1; -> d2.operator=(&d2,d1)void operator=(const Data& d){_year = d._year;_month = d._month;_day = d._day;}

注意以下的区分:     

Data d3(d1); //拷贝构造 -- 一个存在的对象去初始化另一个要创建的对象
        d2 = d1; //赋值重载/复制拷贝 -- 两个已经存在的对象之间赋值

当这样赋值时会报错:

d3 = d2 = d1;
//二元“ = ”: 没有找到接受“void”类型的右操作数的运算符(或没有可接受的转换)

所以进行修改:

所以应该写成以下这样,需要满足值,因为对于d2 = d1;返回值应该是d2,但是要注意返回值怎么写
d2 = d1; -> d2.operator=(&d2,d1)Data operator=(const Data& d)
但是我们发现这种写法是传值返回,
所有的传值返回和传值传参一样,会生成一个拷贝,
自定义类型传值会调用拷贝构造函数

进一步进行修改:
        因为如果出了作用域返回值还在,可以用引用返回:注意d2 = d1,把d1赋给d2,返回值是d2,d2默认传给this。this是指针,所以返回*this得到指针本身。

Data& operator=(const Data& d)
{_year = d._year;_month = d._month;_day = d._day;return *this;
}

     同样要注意,不排除写错了,自己给自己赋值,所以应该这样写:加上if(this != d),就是避免d2 =d1时写成d2 = d2,d2默认传给this的,d1传给const Data& d的,所以如果d = this就是自己给自己赋值了。同样这样的好处是,只会是权限的缩小,不会有权限的扩大。

 Data& operator=(const Data& d){if (this != &d){_year = d._year;_month = d._month;_day = d._day;}return *this;}

        注意:赋值运算符在类中不显式实现时,编译器会生成一份默认的,此时用户在类外再将赋值运算符重载为全局的,就和编译器生成的默认赋值运算符冲突了,故赋值运算符只能重载成成员函数


总结

运算重载符号(C++)

运算重载符号(C++)相关推荐

  1. 计算机系统基础实验 - 同符号浮点数加法运算/无符号定点数乘法运算的机器级表示

    实验3 同符号浮点数加法运算/无符号定点数乘法运算的机器级表示 实验序号:3 实验名称:同符号浮点数加法运算/无符号定点数乘法运算的机器级表示 适用专业:软件工程 学 时 数:2学时 一.实验目的 1 ...

  2. C++学习日记3——友元、运算重载符、继承、多态

    目录 一.友元 1.1 通俗解释 1.2 编程解释 1.3 友元的关键字 1.4 友元的三种实现 1.4.1 全局函数做友元 1.4.2 类做友元 1.4.3 成员函数做友元 二.运算重载符 2.1 ...

  3. matlab整理符号表达式,[2018年最新整理]MATLAB符号运算与符号方程求解.ppt

    [2018年最新整理]MATLAB符号运算与符号方程求解 MATLAB符号计算 1 符号对象 2 符号微积分 3 级 数 4 符号方程求解 9.1 符号对象 9.1.1 建立符号对象 1.建立符号变量 ...

  4. C++ 复数类加减法运算重载为成员函数形式

    运算符的重载形式有两种,即重载为类的非静态成员函数和重载为非成员函数.运算符重载为类的成员函数的一般语法形式为: 返回类型 operator 运算符(形参表){函数体 } 运算符重载为非成员函数的一般 ...

  5. PHP中关于取模运算及符号

    执行程序段<?php  echo 8%(-2) ?>,输出结果是: %为取模运算,以上程序将输出0 $a%$b,其结果的正负取决于$a的符号. echo ((-8)%3);     //将 ...

  6. python幂运算的符号有哪些及画法_SymPy 符号计算基本教程

    SymPy 是一个由 Python 语言编写的符号计算库.我将在本文中简要地介绍如何利用 SymPy 进行符号计算.在介绍 SymPy 之前,我们首先要明确何谓符号计算?计算机代数系统又是什么? 什么 ...

  7. 乘法/积运算和符号(点乘/内积/数量积,叉乘/向量积,矩阵乘法,Hadamard, Kronecker积,卷积)一网打尽

    之前一直混淆于各种乘法和积运算中,不得其解,所以花了点功夫整理一下. 名称 符号 Latex 运算 应用 意义 点乘/内积/数量积 ⋅\cdot⋅或∙\bullet∙ \cdot或\bullet a⃗ ...

  8. python中幂运算的符号是什么_SymPy 符号计算基本教程

    SymPy 是一个由 Python 语言编写的符号计算库.我将在本文中简要地介绍如何利用 SymPy 进行符号计算.在介绍 SymPy 之前,我们首先要明确何谓符号计算?计算机代数系统又是什么? 什么 ...

  9. python同符号数学运算_符号数学Python?

    符号数学是一个有趣的项目.在你的问题上,是否有人使用它似乎无关紧要,所以请投入. 这些年来我写了两本.最酷的是一个for SQL where子句--它对SQL做了一些琐碎的符号操作来折叠一些附加的和条 ...

最新文章

  1. 编写第一个Android程序
  2. 与Android数据库一起工作
  3. 一步一步写算法(之哈希二叉树)
  4. 干货—MySQL常见的面试题+索引原理分析!
  5. 基于VS2015的C#的GDAl环境配置
  6. Mediastream2 用法介绍及简明实例分析
  7. 华为悦盒EC6108V9 、EC6108V9C_1080UI_非高安版_鸿蒙动画_免拆卡刷固件
  8. xdg在Linux中的用法,linux-如何使用sudo获取XDG变量?
  9. FBReader 探究 2
  10. Android 图片处理以及recycle机制
  11. uniform,attribute和varying
  12. C-V2X 技术介绍
  13. keep-alive和activated
  14. 透透彻彻IoC(你没有理由不懂!)
  15. php转调页面,怎样练到转调弹奏信手捏来,太实用
  16. Kubernetes K8S之Taints污点与Tolerations容忍详解
  17. 启动牛市的密钥藏宝计划(TPC),火热来袭!
  18. python需要学英语吗_李易峰吴昕小说18年甜文
  19. nunito字体_dcat-admin: 使用很少的代码快速构建一个功能完善的高颜值后台系统,内置丰富的后台常用组件,开箱即用,让开发者告别冗杂的HTML代码。...
  20. FBI树——递归练习

热门文章

  1. Vue引用原生高德地图标注
  2. win7设置防火墙允许Ping与telnet
  3. springboot企业人力资源管理系统毕业设计源码291816
  4. 视频拼接软件哪个好用?这些软件媒体人都喜欢
  5. 使用python爬虫爬取百度新闻,告诉你社会热点话题
  6. SQL查询语句——子查询
  7. 【Linux】Linux权限管理————shell运行原理 | Linux权限管理 | 粘滞位 | 权限掩码umask
  8. SQL(16)--获取员工当前薪水比其manager薪水还高的相关信息
  9. 【python量化交易学习】pandas获取mysql数据,使用pyecharts画K线图,ma移动均线。
  10. 软件工程领域CCF B类会议:SANER介绍(以SANER 2019为例)