类和对象:运算符重载

 本篇文章来介绍一下C++中的运算符重载,以及与运算符重载有关的三个默认默认成员函数:赋值运算符重载普通对象取地址与const对象取地址操作符重载,也就是下面图片中6个默认成员函数的后三个,前三个默认成员函数在之前文章中已经讲过类和对象:构造函数,析构函数与拷贝构造函数_一棵西兰花的博客-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/qq_72916130/article/details/132789343?spm=1001.2014.3001.5501,大家不太清楚的可以去看一下。

本篇文章主要通过一个日期类来进行讲解。我会把完整的日期类放到后面。

class Date
{
public://...
private:int _year;int _month;int _day;
};

1.什么是运算符重载

C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。

函数名字为:关键字operator后面接需要重载的运算符符号。

函数原型:返回值类型 operator操作符(参数列表)

注意:

  • 不能通过连接其他符号来创建新的操作符:比如operator@
  • 重载操作符必须有一个类类型参数
  • 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义
  • 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this指针
  • .*   ::   sizeof    ? :    . 注意以上5个运算符不能重载。这个经常在笔试选择题中出现。

光看定义是不行的,我们实现以下 > 和 == 的重载

// >运算符重载
//因为>号有两个操作数,因为有一个是隐藏的this指针,所以这里只有一个参数
bool Date::operator>(const Date& d)
{if (_year < d._year){return false;}else if (_year == d._year && _month < d._month){return false;}else if (_year == d._year && _month == d._month && _day <= d._day){return false;}else{return true;}
}// ==运算符重载
bool Date::operator==(const Date& d)
{return _year == d._year && _month == d._month && _day == d._day;
}

有一个 > 和 == 或< 和 == ,其他比较运算符就可以有这两个推导出来,不用再一个一个写。


2.赋值运算符重载

1.赋值运算符重载格式

  • 参数类型:const T&,传递引用可以提高传参效率,加const是为了防止参数被修改
  • 返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
  • 检测是否自己给自己赋值
  • 返回*this :要符合连续赋值的含义

代码:

Date& Date::operator=(const Date& d)//可以不用引用,但没有必要
{if (this != &d)//自己给自己赋值{_year = d._year;_month = d._month;_day = d._day;}return *this;
}

对于返回*this,因为我们平时的赋值运算符可以实现 类似与 a = b = c = d 的方式,显示调用就是a.operator(b.operator(c.operator(d))),如果operator=函数没有返回值,就会出现错误。

2.赋值运算符只能重载成类的成员函数不能重载成全局函数

// 赋值运算符重载成全局函数,注意重载成全局函数时没有this指针了,需要给两个参数
Date& operator=(Date& left, const Date& right)
{if (&left != &right){left._year = right._year;left._month = right._month;left._day = right._day;}return left;
}// 编译失败:
// error C2801: “operator =”必须是非静态成员

原因:赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的成员函数。

3.用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。注意:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。

既然编译器生成的默认赋值运算符重载函数已经可以完成字节序的值拷贝了,还需要自己实
现吗?当然像日期类这样的类是没必要的。

注意:如果类中未涉及到资源管理,赋值运算符是否实现都可以;一旦涉及到资源管理则必须要实现。因为他会将地址也进行简单的值拷贝(浅拷贝),在进行析构时回释放两次,会导致程序崩溃。


3.前置++与后置++重载

前置++与后置++的操作数只有一个,而且只有返回值不同,一个前置++返回自己加一,后置++需要在构造一个Date 来存放没有加一时的结果进行返回。只有返回值不同,没有办法区分两个函数,只好在后置++加入一个占位参数,没有什么用途,只是为了区分前置与后置,编译器也是通过这个方式来识别。

C++规定:后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器自动传递

代码: 

// 前置++
Date& Date::operator++()
{*this += 1;return *this;
}// 注意:后置++是先使用后+1,因此需要返回+1之前的旧值,
//故需在实现时需要先将this保存一份,然后给this+1
// 而temp是临时对象,因此只能以值的方式返回,不能返回引用
// 后置++ 传入的值不用接收
Date Date::operator++(int)
{Date tmp(*this);*this += 1;return tmp;
}

-- 也一样:        

// 后置--
Date Date::operator--(int)
{Date tmp(*this);*this -= 1;return tmp;
}
// 前置--
Date& Date::operator--()
{*this -= 1;return *this;
}

4.const成员

将const修饰的“成员函数”称之为const成员函数,const修饰类成员函数,这里的const需要加在形参列表的后面,实际修饰该成员函数隐含的this指针表明在该成员函数中不能对类的任何成员进行修改。

我们来看看下面的代码:

这里类成员函数Print() 没有用const修饰。

    const Date d1(2023, 9, 23);//const修饰的对象调用不了成员函数,因为权限放大d1.Print();//d1.print(&d1) 隐藏的传入this的指针是const Data* 类型

Print() 没有被const 修饰,函数形参this指针是 普通的 Date* 类型,而被const修饰的对象调用Print()函数,传入的this指针是const Data* 类型 ,会发生权限的放大,权限可以缩小,平移,但是不能放大,会报错。

请思考下面的几个问题:

  1. const对象可以调用非const成员函数吗?
  2. 非const对象可以调用const成员函数吗?
  3. const成员函数内可以调用其它的非const成员函数吗?
  4. 非const成员函数内可以调用其它的const成员函数吗?

答案:1,3不可以,权限放大。2,4可以,权限缩小。

我们可以通过函数重载使不同权限的对象调用不同的函数。如下面代码的Print:

class Date
{
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}void Print(){cout << "Print()" << endl;cout << "year:" << _year << endl;cout << "month:" << _month << endl;cout << "day:" << _day << endl << endl;}void Print() const{cout << "Print()const" << endl;cout << "year:" << _year << endl;cout << "month:" << _month << endl;cout << "day:" << _day << endl << endl;}
private:int _year; // 年int _month; // 月int _day; // 日
};
void Test()
{Date d1(2022,1,13);d1.Print();const Date d2(2022,1,13);d2.Print();
}

当然Print函数用const 修饰是没有什么意义的。我们看下面的取地址及const取地址操作符重载。


5.取地址及const取地址操作符重载

这两个默认成员函数一般不用重新定义 ,编译器默认会生成

class Date
{
public ://这种方式返回指针的内容可以修改,可读可写Date* operator&(){return this ;}//这种方式返回指针的内容也不能修改,只读const Date* operator&()const{return this ;}
private :int _year ; // 年int _month ; // 月int _day ; // 日
};

这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需要重载,比如想让别人获取到指定的内容,不返回this,返回nullptr等等。

6. Date 类的实现

Date.h 

class Date
{public:// 获取某年某月的天数int GetMonthDay(int year, int month);//全缺省的构造函数Date(int year = 1900, int month = 1, int day = 1);// 赋值运算符重载// d2 = d3 -> d2.operator=(&d2, d3)Date& operator=(const Date& d);// 日期+=天数Date& operator+=(int day);// 日期+天数Date operator+(int day);// 日期-天数Date operator-(int day);// 日期-=天数Date& operator-=(int day);void Print();//下面两个是运算符重载,也构成函数重载// 前置++ //++d1 -> d1.operator++()Date& operator++();// 后置++//d1++ -> d1.operator++(0)//传一个整形即可,只是用来区分前置++//加了一个int参数,进行占位,跟前置++构成函数重载进行区分//本质后置++调用,编译器进行了特殊处理Date operator++(int);//传入的值不用接收// 后置--Date operator--(int);// 前置--Date& operator--();// >运算符重载bool operator>(const Date& d);// ==运算符重载bool operator==(const Date& d);// >=运算符重载bool operator >= (const Date& d);// <运算符重载bool operator < (const Date& d);// <=运算符重载bool operator <= (const Date& d);// !=运算符重载bool operator != (const Date& d);// 日期-日期 返回天数int operator-(const Date& d);const Date* operator&()const;Date* operator&();private:int _year;int _month;int _day;
};

Date.cpp

// 获取某年某月的天数
int Date::GetMonthDay(int year, int month)
{static int MonthDay[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };if (month == 2 && (year % 4 == 0 && year % 100 != 0 || year % 400 == 0)){return 29;}return MonthDay[month];
}//全缺省的构造函数
//声明和定义缺省参数的值,不能都给,必须在声明中给,
//因为在编译时头文件中看到的是声明,而声明中没有提供缺省值,会报错。到不了后面的链接,通过函数名修饰规则来找到函数的定义
Date::Date(int year, int month, int day)
{_year = year;_month = month;_day = day;//检查日期日期是否合法if (month < 1 || month>12 || day < 1||_day > GetMonthDay(_year, _month)){cout << "非法日期\n" << endl;exit(-1);}
}// 赋值运算符重载
// d2 = d3 -> d2.operator=(&d2, d3)
Date& Date::operator=(const Date& d)//可以不用引用,但没有必要
{if (this != &d)//自己给自己赋值{_year = d._year;_month = d._month;_day = d._day;}return *this;
}// 日期+=天数
Date& Date::operator+=(int day)//0次拷贝 0次赋值
{if (day < 0){return *this -= (-day);}_day += day;while (_day > GetMonthDay(_year, _month)){_day -= GetMonthDay(_year, _month);_month++;while (_month > 12){_month = 1;_year++;}}return *this;
}
// 日期+天数
Date Date::operator+(int day)//两次拷贝
{if (day < 0){return *this - (-day);}Date ret(*this);ret += day;return ret;
}// 日期-天数
Date Date::operator-(int day)
{if (day < 0){return *this + (-day);}Date ret = *this;ret -= day;return ret;
}// 日期-=天数
Date& Date::operator-=(int day)
{if (day < 0){return *this += (-day);}_day -= day;while (_day < 1){_month--;while (_month < 1){_month = 12;_year--;}_day += GetMonthDay(_year, _month);}return *this;
}//只读函数可以加const,内部不涉及修改成员//void Date::Print(const Data* this)
//void Date::Print() //可以同时存在,因为this指针类型不同,如果只有const版本也可调用,因为权限可以缩小
//{//不过在这里没有意义
//	cout << _year << "/" << _month << "/" << _day << endl;
//}
void Date::Print() const
{cout << _year << "/" << _month << "/" << _day << endl;
}// 前置++
Date& Date::operator++()
{*this += 1;return *this;
}// 后置++
Date Date::operator++(int)
{Date tmp(*this);*this += 1;return tmp;
}
// 后置--
Date Date::operator--(int)
{Date tmp(*this);*this -= 1;return tmp;
}
// 前置--
Date& Date::operator--()
{*this -= 1;return *this;
}// >运算符重载
bool Date::operator>(const Date& d)
{if (_year < d._year){return false;}else if (_year == d._year && _month < d._month){return false;}else if (_year == d._year && _month == d._month && _day <= d._day){return false;}else{return true;}
}// ==运算符重载
bool Date::operator==(const Date& d)
{return _year == d._year && _month == d._month && _day == d._day;
}// >=运算符重载
bool Date::operator >= (const Date& d)
{return *this > d || *this == d;
}// <运算符重载
bool Date::operator < (const Date& d)
{return !(*this >= d);
}// <=运算符重载
bool Date::operator <= (const Date& d)
{return !(*this > d);
}// !=运算符重载
bool Date::operator != (const Date& d)
{return !(*this == d);
}//跟日期-天数构成函数重载
// 日期-日期 返回天数
int Date::operator-(const Date& d)
{Date max = *this;Date min = d;int flag = 1;if (max < min){min = *this;max = d;flag = -1;}int count = 0;while (max != min){--max;count++;}return count * flag;
}//日常自动生成的就可以
//不要被取到有效地址
//const Date* Date::operator&()const
//{
//	return nullptr;
//}
const Date* Date::operator&()const
{return this;
}
//这个接口是读写
Date* Date::operator&()//函数重载,一个给const对象用,一个给非const对象用
{return this;
}

本篇结束!

 

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

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

相关文章

【go语言】结构体

结构体 结构体定义 type name struct{value1 type1value2 type2...... }组成结构体的数据称为字段&#xff0c;每一个字段有一个类型和一个名字&#xff0c;每一个字段的名字必须唯一&#xff0c;字段的类型任意。 创建结构体 type myStruct struct{i1 intf1 float32str st…

HDLBits-Edgedetect

刚开始写的代码如下&#xff1a; module top_module (input clk,input [7:0] in,output [7:0] pedge );reg [7:0] in_pre;always (posedge clk)begin in_pre < in;endassign pedge in & ~in_pre; endmodule但是提交结果是错误的。猜想原因如下&#xff1a; assign p…

某音网页端 X-Bogus 参数

逆向目标 目标&#xff1a;某音网页端用户信息接口 X-Bogus 参数 接口&#xff1a;aHR0cHM6Ly93d3cuZG91eWluLmNvbS9hd2VtZS92MS93ZWIvdXNlci9wcm9maWxlL290aGVyLw 什么是 JSVMP&#xff1f; JSVMP 全称 Virtual Machine based code Protection for JavaScript&#xff0c;即 …

【网络八股】TCP八股

网络八股 请简述TCP/IP模型中每层的作用&#xff0c;典型协议和典型设备介绍一下三次握手的过程介绍一下四次挥手的过程必须三次握手吗&#xff0c;两次不行吗&#xff1f;为什么ACK数据包消耗TCP的序号吗三次握手中可以携带应用层数据吗四次挥手时&#xff0c;可以携带应用层数…

crypto:丢失的MD5

题目 得到一个md5.py 运行一下&#xff0c;发现报错&#xff0c;修改一下 运行之后又报错 报错原因是算法之前编码 正确的代码为 import hashlib for i in range(32,127):for j in range(32,127):for k in range(32,127):mhashlib.md5()m.update((TASC chr(i) O3RJMV c…

【Java SE】Lambda表达式

目录 ♫什么是Lambda表达式 ♫Lambda表达式的语法 ♫函数式接口 ♫Lambda表达式的使用 ♫变量捕获 ♫ Lambda表达式在集合中的使用 ♪Collection的foreach()&#xff1a; ♪List的sort()&#xff1a; ♪Map的foreach() ♫什么是Lambda表达式 Lambda 表达式是 Java SE 8中一个…

SpringMVC 学习(二)Hello SpringMVC

3. Hello SpringMVC (1) 新建 maven 模块 springmvc-02-hellomvc (2) 确认依赖的导入 (3) 配置 web.xml <!--web/WEB-INF/web.xml--> <?xml version"1.0" encoding"UTF-8"?> <web-app xmlns"http://xmlns.jcp.org/xml/ns/javaee…

通用CI/CD软件平台TeamCity推出代理终端功能,谁能从中获益?

JetBrains官方在TeamCity中推出代理终端&#xff1a;这项新功能专门用于帮助用户轻松查看代理上的系统日志、检查已安装的软件&#xff0c;以及直接从 TeamCity 的 UI 调试特定代理问题。 TeamCity是一个通用的 CI/CD 软件平台&#xff0c;可以实现灵活的工作流、协作和开发做…

抖音短视频seo矩阵系统源代码开发系统架构及功能解析

短视频seo源码&#xff0c;短视频seo矩阵系统底层框架上支持了从ai视频混剪&#xff0c;视频批量原创产出&#xff0c;云存储批量视频制作&#xff0c;账号矩阵&#xff0c;视频一键分发&#xff0c;站内实现关键词、短视频批量搜索排名&#xff0c;数据统计分类多功能细节深度…

深入MySQL数据库进阶实战:性能优化、高可用性与安全性

&#x1f482; 个人网站:【工具大全】【游戏大全】【神级源码资源网】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 寻找学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】 MySQL是世界上最流行的开…

ruoyi-nbcio增加websocket与测试页面

更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码&#xff1a; https://gitee.com/nbacheng/ruoyi-nbcio 为了后面流程发起等消息推送&#xff0c;所以需要集成websocket。 1、后端增加websoket支持 首先在framework模块里的pom.xml增加websocket <dependency…

Element UI搭建首页导航和左侧菜单以及Mock.js和(组件通信)总线的运用

目录 前言 一、Mock.js简介及使用 1.Mock.js简介 1.1.什么是Mock.js 1.2.Mock.js的两大特性 1.3.Mock.js使用的优势 1.4.Mock.js的基本用法 1.5.Mock.js与前端框架的集成 2.Mock.js的使用 2.1安装Mock.js 2.2.引入mockjs 2.3.mockjs使用 2.3.1.定义测试数据文件 2…

如何优化网站排名(百度SEO指南与优化布局方法)

百度SEO指南介绍&#xff1a;蘑菇号-www.mooogu.cn 首先&#xff0c;为了提高网站的搜索引擎优化排名&#xff0c;需要遵循百度SEO指南的规则和标准。这包括使用符合规范的网站结构、页面内容的质量和与目标用户相关的关键词。避免使用非法技术和黑帽SEO的方法。 增加百度SEO…

Python——— 异常机制

&#xff08;一&#xff09;异常 工作中&#xff0c;程序遇到的情况不可能完美。比如&#xff1a;程序要打开某个文件&#xff0c;这个文件可能不存在或者文件格式不对&#xff1b;程序在运行着&#xff0c;但是内存或硬盘可能满了等等。 软件程序在运行过程中&#xff0c;非常…

【计算机网络】网络层和数据链路层

文章目录 IP协议网段划分分类划分法CIDR 方案路由NAT网络地址转换技术IP报文的另外三个参数mac帧ARP协议交换机ICMP代理服务器 IP协议 TCP有将数据 可靠、高效 发给对方的 策略&#xff0c;而IP具有发送的能力&#xff0c;即将数据从A主机送到B主机的 能力 。 用户要的是100%…

程序员不得不知道的排序算法-上

目录 前言 1.冒泡排序 2.选择排序 3.插入排序 4.希尔排序 5.快速排序 6.归并排序 总结 前言 今天给大家讲一下常用的排序算法 1.冒泡排序 冒泡排序&#xff08;Bubble Sort&#xff09;是一种简单的排序算法&#xff0c;它重复地从待排序的元素中比较相邻的两个元素&a…

vue event bus 事件总线

vue event bus 事件总线 创建 工程&#xff1a; H:\java_work\java_springboot\vue_study ctrl按住不放 右键 悬着 powershell H:\java_work\java_springboot\js_study\Vue2_3入门到实战-配套资料\01-随堂代码素材\day04\准备代码\08-事件总线-扩展 vue --version vue crea…

【C语言】文件操作(一)

前言 本篇博客讲解对文件的操作&#xff0c;包括打开&#xff0c;关闭操作。在下篇博客将讲解文件的读写。 文章目录 一、 什么是文件&#xff1f;1.1 用于存储数据1.2 文件类型1.3 文件名1.4 二进制文件和文本文件 二、文件的打开和关闭2.1 流和标准流2.2 文件指针2.3文件的打…

你的周末和你一起失去了价值(打工人篇)

花儿在绽放盛开之前&#xff0c;会在无人的清晨吸收甘露&#xff0c;然后赶上第一趟的朝阳&#xff0c;才换来路人赞许 一言指南北 选择你的职业&#xff0c;确认你的方向&#xff0c;没有方向&#xff0c;就无法体验时间感 如果你是打工人&#xff0c;那么请接着往下看 如果是…

React项目中如何实现一个简单的锚点目录定位

小册 这是我整理的学习资料&#xff0c;非常系统和完善&#xff0c;欢迎一起学习 现代JavaScript高级小册 深入浅出Dart 现代TypeScript高级小册 linwu的算法笔记&#x1f4d2; 前言 锚点目录定位功能在长页面和文档类网站中非常常见,它可以让用户快速定位到页面中的某个…