【C++】类和对象(5)--运算符重载

目录

一 概念

二 运算符重载的实现

三 关于时间的所有运算符重载

四 默认赋值运算符

五 const取地址操作符重载


一 概念

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

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

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

注意:

不能通过连接其他符号来创建新的操作符:比如operator@

重载操作符必须有一个类类型参数

用于内置类型的运算符,其含义不能改变,例如:内置的整型 + ,不能改变其含义

作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this

总之如何去比较自定义类型, 并且要有可读性, 那就需要运算符重载

二 运算符重载的实现

// 全局的operator==
class Date
{
public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}//private:int _year;int _month;int _day;
};
// 这里会发现运算符重载成全局的就需要成员变量是公有的,那么问题来了,封装性如何保证?
// 这里其实可以用我们后面学习的友元解决,或者干脆重载成成员函数。
bool operator==(const Date& d1, const Date& d2)
{return d1._year == d2._year//如果成员变量不是共有的那就访问不到了&& d1._month == d2._month&& d1._day == d2._day;
}
void Test1()
{Date d1(2018, 9, 26);Date d2(2018, 9, 27);/*cout << operator>(d1, d2) << endl;cout << operator==(d1, d2) << endl;*/cout << (d1 == d2) << endl;
}int main()
{Test1();return 0;
}

现在我们把==运算符重载到成员函数中

// 全局的operator==
class Date
{
public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}bool operator==(const Date& dd2){return _year == dd2._year&& _month == dd2._month&& _day == dd2._day;      }private:int _year;int _month;int _day;
};void Test2()
{Date d1(2018, 9, 26);Date d2(2018, 9, 27);/*cout << operator>(&d1, d2) << endl;cout << operator==(&d1, d2) << endl;*/cout << (d1 == d2) << endl;
}int main()
{Test2();return 0;
}

总结:

运算符重载 函数重载 他们之间没有关联
运算符重载:自定义类型可以直接使用运算符
函数重载:可以允许参数不同的同名函数,

内置类型对象可以直接用各种运算符,内置类型都是简单类型
语言自己定义,编译直接转换成指令
自定义类型呢?不支持  所以运算符重载诞生
不能被重载的运算符只有5个, 点号.三目运算 ? : 作用域访问符::运算符sizeof  以及.*(*是可以重载的 只是点星是不能的)
 

三 关于时间的所有运算符重载

1 Date.h

#pragma once#include<iostream>
#include<assert.h>
using namespace std;class Date
{
public://全缺省参数只需要在声明中Date(int year = 1, int month = 1, int day = 1);void Print();int GetMonthDay(int year, int month);Date& operator=(const Date& d);bool operator==(const Date& y);bool operator!=(const Date& y);bool operator>(const Date& y);bool operator<(const Date& y);bool operator>=(const Date& y);bool operator<=(const Date& y);int operator-(const Date& d);Date& operator+=(int day);Date operator+(int day);Date& operator-=(int day);Date operator-(int day);Date& operator++();Date operator++(int);Date& operator--();Date operator--(int);//友元函数friend ostream& operator<<(ostream& out, const Date& d);friend istream& operator>>(istream& in, Date& d);private:int _year;int _month;int _day;
};ostream& operator<<(ostream& out, const Date& d);
istream& operator>>(istream& in, Date& d);

2 Date.cpp

#define _CRT_SECURE_NO_WARNINGS 1
#include"Date.h"int Date:: GetMonthDay(int year, int month)
{int days[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };int day = days[month];if (month == 2 && (year % 4 == 0 && year % 100 != 0) && (year % 100 == 0)){day += 1;}return day;
}//构造函数
Date:: Date(int year, int month, int day)
{_year = year;_month = month;_day = day;
}void Date::Print()
{cout << _year << "/" << _month << "/" << _day << endl;
}//赋值运算符
Date& Date::operator=(const Date& d)
{if (*this != d){_year = d._year;_month = d._month;_day = d._day;}return *this;
}
//赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的赋值运算符重载,
//就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的成员函数bool Date::operator==(const Date& d)
{return (_day == d._day && _month == d._month && _year == d._year);
}bool Date::operator!=(const Date& d)
{return !(*this == d);
}bool Date::operator>(const Date& d)
{if (_year > d._year){return true;}else if (_year == d._year && _month > d._month){return true;}else if (_year == d._year && _month == d._month && _day > d._day){return true;}else{return false;}}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);
}//在类里面是不用区分函数顺序的
Date& Date::operator+=(int day)
{if (day < 0){return *this -= (-day);}_day = _day + day;while (_day > GetMonthDay(_year, _month)){_day -= GetMonthDay(_year, _month);++_month;if (_month == 13){++_year;_month = 1;}}return *this;
}Date Date:: operator+(int day)
{Date tmp(*this);tmp += day;return tmp;
}Date& Date:: operator-=(int day)
{if (day < 0){*this += (-day);}_day -= day;while (_day < 0){--_month;if (_month == 0){--_year;_month = 12;}_day += GetMonthDay(_year, _month);}return *this;
}Date Date:: operator-(int day)
{Date tmp(*this);tmp -= day;return tmp;
}//++d1
Date& Date::operator++()
{*this += 1;return *this;
}//d1++
Date Date::operator++(int)
{Date tmp(*this);*this += 1;return tmp;
}//--d1
Date& Date::operator--()
{*this -= 1;return *this;
}//d1--
Date Date::operator--(int)
{Date tmp(*this);*this -= 1;return tmp;
}//d1 -100
int Date::operator-(const Date& d)
{//假设左大右小int flag = 1;Date max = *this;Date min = d;if (*this < d){flag = -1;min = *this;max = d;}int n = 0;while (min != max){min++;n++;}return n * flag;
}ostream& operator<<(ostream& out, const Date& d)
{out << d._year << "年" << d._month << "月" << d._day << "日" << endl;return out;
}istream& operator>>(istream& in, Date& d)
{in >> d._year >> d._month >> d._day;return in;
}

 3 Test.cpp

#define _CRT_SECURE_NO_WARNINGS 1
#include "Date.h"void TestDate1()
{Date d1(2023, 10, 24);d1.Print();Date ret1 = d1 - 100;ret1.Print();Date ret2 = d1 - 10000;ret2.Print();Date ret3 = d1 + 100;ret3.Print();Date ret4 = d1 + 10000;ret4.Print();
}void TestDate2()
{Date d1(2023, 10, 24);d1.Print();// 语法设计,无法逻辑闭环,那么这时就只能特殊处理// 特殊处理++d1;d1.operator++();d1.Print();d1++;d1.operator++(10);d1.operator++(1);d1.Print();
}void TestDate3()
{Date d1(2023, 10, 24);d1.Print();Date d2(2024, 5, 5);d2.Print();Date d3(2024, 8, 1);d3.Print();cout << d2 - d1 << endl;cout << d1 - d3 << endl;}void TestDate4()
{Date d1(2023, 10, 24);d1 += -100;d1.Print();
}void Test5()
{Date d1(2023, 10, 21);Date d2(2023, 12, 31);d1.Print();cout << d1;cin >> d2;cout << d2 << d1 << endl;
}int main()
{TestDate1();TestDate2();TestDate3();TestDate4();Test5();return 0;
}

解释一下<< >>运算符重载

 

四 默认赋值运算符

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

Date MyStack这些就不用自己构造赋值运算符重载, 但是栈这些就必须要自己构造, 因为涉及到了资源的拷贝

注意:如果类中未涉及到资源管理,赋值运算符是否实现都可以;一旦涉及到资源管理则必 须要实现。

// 这里会发现下面的程序会崩溃 这里就需要我们以后讲的深拷贝去解决。
typedef int DataType;
class Stack
{
public:Stack(size_t capacity = 10){_array = (DataType*)malloc(capacity * sizeof(DataType));if (nullptr == _array){perror("malloc申请空间失败");return;}_size = 0;_capacity = capacity;}void Push(const DataType& data){// CheckCapacity();_array[_size] = data;_size++;}~Stack(){if (_array){free(_array);_array = nullptr;_capacity = 0;_size = 0;}}
private:DataType* _array;size_t _size;size_t _capacity;
};
int main()
{Stack s1;s1.Push(1);s1.Push(2);s1.Push(3);s1.Push(4);Stack s2;s2 = s1;return 0;
}

改正如下

typedef int DataType;
class Stack
{
public:Stack(size_t capacity = 10){_array = (DataType*)malloc(capacity * sizeof(DataType));if (nullptr == _array){perror("malloc申请空间失败");return;}_size = 0;_capacity = capacity;}void Push(const DataType& data){// CheckCapacity();_array[_size] = data;_size++;}void Pop(){_size--;}DataType Top(){return _array[_size - 1];}bool Empty(){return _size == 0;}Stack& operator=(Stack& st){_array = (int*)malloc(sizeof(int) * st._capacity);if (_array == nullptr){perror("malloc fail");exit(-1);}memcpy(_array, st._array, sizeof(int) * st._size);_size = st._size;_capacity = st._capacity;}~Stack(){if (_array){free(_array);_array = nullptr;_capacity = 0;_size = 0;}}
private:DataType* _array;size_t _size;size_t _capacity;
};
int main()
{Stack s1;s1.Push(1);s1.Push(2);s1.Push(3);s1.Push(4);while (!s1.Empty()){printf("%d ", s1.Top());s1.Pop();}printf("\n");Stack s2;s2 = s1;s2.Push(5);s2.Push(6);s2.Push(7);s2.Push(8);while (!s2.Empty()){printf("%d ", s2.Top());s2.Pop();}return 0;
}

讲一下为什么

五 const取地址操作符重载

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

       Date* operator&() //返回类型为 Date*{cout << "Date* operator&()" << endl;return this;}const Date* operator&()const//返回类型为 const Date*{cout << "const Date* operator&()const" << endl;return this;}

当然不是const的地址也可以调用const类型, 只不过两个都存在的时候, 会优先调用最匹配的一个

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

本节感觉理解起来还是比较抽象, 有时候记住咋用就行了, 祖师爷就是这样规定的, 但是底层的东西我们还是得好好琢磨一下.对于类和对象基础要求挺高. 大家可以看看我之前的博客.

继续加油!

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

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

相关文章

UE5 C++报错:is not currently enabled for Live Coding

解决办法&#xff1a; 再次打开项目&#xff0c;以此法打开&#xff1a;

ToolJet:开源低代码框架,轻松构建复杂可响应界面 | 开源日报 No.78

ToolJet/ToolJet Stars: 25.0k License: AGPL-3.0 ToolJet 是一个开源的低代码框架&#xff0c;可以通过最小化工程投入来构建和部署内部工具。ToolJet 的拖放式前端构建器允许您在几分钟内创建复杂、响应式的前端界面。此外&#xff0c;您还可以集成各种数据源&#xff0c;包…

【Linux网络编程】高级I/O

目录 五种I/O模型 阻塞和非阻塞 非阻塞I/O I/O多路复用之Select、Poll、与Epoll 本文目的是深入浅出理解高级I/O相关的知识&#xff0c;结尾附上代码加深理解相关知识。 五种I/O模型 1.阻塞I/O&#xff1a;在内核将数据准备好之前&#xff0c;系统调用会一直等待。所有的套…

设计模式(5)-使用设计模式实现简易版springIoc

自定义简易版springIoc 1 spring使用回顾 自定义spring框架前&#xff0c;先回顾一下spring框架的使用&#xff0c;从而分析spring的核心&#xff0c;并对核心功能进行模拟。 数据访问层。定义UserDao接口及其子实现类 public interface UserDao {public void add(); }public…

基于静电放电算法优化概率神经网络PNN的分类预测 - 附代码

基于静电放电算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于静电放电算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于静电放电优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;针对PNN神…

微信个人号api

简要描述&#xff1a; 登录E云平台 请求URL&#xff1a; http://域名地址/member/login域名地址开发者账号密码:后台系统自助开通 请求方式&#xff1a; POST 请求头Headers&#xff1a; Content-Type&#xff1a;application/json 参数&#xff1a; 参数名必选类型说…

​如何使用ArcGIS Pro制作渐变河流效果

对于面要素的河流水系&#xff0c;制作渐变效果方法比较简单&#xff0c;如果是线要素的河流有办法制作渐变效果吗&#xff0c;答案是肯定的&#xff0c;这里为大家介绍一下制作方法&#xff0c;希望能对你有所帮助。 数据来源 本教程所使用的数据是从水经微图中下载的水系数…

《网络协议》07. 其他协议

title: 《网络协议》07. 其他协议 date: 2022-10-07 18:24:02 updated: 2023-11-15 08:00:52 categories: 学习记录&#xff1a;网络协议 excerpt: IPv6、WebSocket、WebService&#xff08;SOAP&#xff0c;WSDL&#xff09;、HTTPDNS、FTP、邮件&#xff08;SMTP&#xff0c;…

基于单片机的温度控制器系统设计

**单片机设计介绍&#xff0c; 基于单片机的温度控制器系统设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机的温度控制器系统是一种利用单片机来检测环境温度并控制温度的系统。它通常由以下几个部分组成&#xff…

Spring 设计模式-简洁版

Java 中包括以下设计模式&#xff1a; 其中Spring 用到的设计模式 1.简单工厂-BeanFactory 2.工厂方法FactoryBean 3.单例模式Bean实例 4.适配器模式SpringMVC中的HandlerAdatper 5.装饰器模式BeanWrapper 6.代理模式_AOP底层 7.观察者模式-spring的事件监听 8.策略横式exclud…

基于人工电场算法优化概率神经网络PNN的分类预测 - 附代码

基于人工电场算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于人工电场算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于人工电场优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;针对PNN神…

jedis连接redis

package com.wsd;import redis.clients.jedis.Jedis;import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.Map; import java.util.Properties;public class Redis {public static void main(String[] args) {//读取properti…

Windows11 python3.12 安装pyqt6 pyqt6-tools

Windows11 python3.12 安装pyqt6比较容易&#xff0c;但pyqt6-tools一直安装不上去。出错信息如下&#xff1a; (venv) PS D:\python_project\pyqt6> pip install pyqt6-tools Collecting pyqt6-toolsUsing cached pyqt6_tools-6.4.2.3.3-py3-none-any.whl (29 kB) Collec…

思源笔记的优缺点 vs Obsidian vs Logseq vs Trilium

新用户对思源笔记的印象。&#xff08;PS&#xff1a;两年前我试用过思源笔记&#xff0c;被卡顿劝退了&#xff09; 优点 相比obsidian&#xff0c; 可在文档树拖拽 拖拽调整笔记顺序 拖拽使一个笔记成为另一个笔记的子笔记&#xff0c;树状结构 设置-文档树&#xff0c;默认…

homeassiant主题

下载主题 https://github.com/maartenpaauw/home-assistant-community-themes.git 使用file editor到homeassiant路径下&#xff0c;新建文件夹themes文件夹&#xff0c;用terminal新建也可以。 使用file editor上传文件 使用Terminal解压 mkdir themes unzip home-assistan…

Redis(消息队列Stream)

Stream是一个轻量级的消息队列。 Redis中Stream的作用是提供一种高效的消息传递机制&#xff0c;允许多个消费者并行地消费消息&#xff0c;并且不会重复消费已经处理过的消息。它可以用于实现分布式任务队列、日志收集、实时数据处理等场景。Redis中的Stream支持多个消费者组…

RabbitMQ 消息丢失解决 (高级发布确认、消息回退与重发、备份交换机)

目录 一、发布确认SpringBoot版本 确认机制图例&#xff1a; 代码实战&#xff1a; 代码架构图&#xff1a; 1.1交换机的发布确认 添加配置类 消息消费者 消息生产者发布消息后的回调接口 测试&#xff1a; 1.2回退消息并重发&#xff08;队列的发布确认&#xff09; …

完整版解答!2023年数维杯国际大学生数学建模挑战赛B题

B题完整版全部5问&#xff0c;问题解答、代码&#xff0c;完整论文、模型的建立和求解、各种图表代码已更新&#xff01; 大家好&#xff0c;目前已完成2023数维杯国际赛B题全部5问的代码和完整论文已更新&#xff0c;部分展示如下&#xff1a; 部分解答图表 问题分析 B题前三…

TensorFlow案例学习:图片风格迁移

准备 官方教程&#xff1a; 任意风格的快速风格转换 模型下载地址&#xff1a; https://tfhub.dev/google/magenta/arbitrary-image-stylization-v1-256/2 学习 加载要处理的内容图片和风格图片 # 用于将图像裁剪为方形def crop_center(image):# 图片原始形状shape image…

计算机视觉:人脸识别与检测

目录 前言 识别检测方法 本文方法 项目解析 完整代码及效果展示 前言 人脸识别作为一种生物特征识别技术&#xff0c;具有非侵扰性、非接触性、友好性和便捷性等优点。人脸识别通用的流程主要包括人脸检测、人脸裁剪、人脸校正、特征提取和人脸识别。人脸检测是从获取的图…