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

目录

一 概念

二 运算符重载的实现

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

四 默认赋值运算符

五 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;
}

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

 

对友元函数不太了解的 可以【C++】类和对象(7)--友元, static成员-CSDN博客

四 默认赋值运算符

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

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.mfbz.cn/a/163410.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Git 简介及使用

前言 假设有这样一个场景&#xff0c;老板让员工做一个档案&#xff0c;员工这个档案做好了之后交给老板看&#xff0c;此时老板不满意&#xff0c;又让回去改&#xff0c;改完给老板看&#xff0c;但是老板又不是很满意&#xff0c;就这样改了又改&#xff0c;给老板看过之后&…

代码随想录算法训练营第四十五天| 139.单词拆分

文档讲解&#xff1a;代码随想录 视频讲解&#xff1a;代码随想录B站账号 状态&#xff1a;看了视频题解和文章解析后做出来了 139.单词拆分 class Solution:def wordBreak(self, s: str, wordDict: List[str]) -> bool:n len(s)dp [False] * (n1)dp[0] Truemax_len m…

WMS重力式货架库位对应方法

鉴于重力式货架的特殊结构和功能&#xff0c;货物由高的一端存入&#xff0c;滑至低端&#xff0c;从低端取出。所以重力式货架的每个货位在物理上都会有一个进货口和一个出货口。因此&#xff0c;在空间上&#xff0c;对同一个货位执行出入库操作需要处于不同的位置。 比如对…

【AD封装】芯片IC-SOP,SOIC,SSOP,TSSOP,SOT(带3D)

包含了我们平时常用的芯片IC封装&#xff0c;包含SOP,SOIC,SSOP,TSSOP,SOT&#xff0c;总共171种封装及精美3D模型。完全能满足日常设计使用。每个封装都搭配了精美的3D模型哦。 ❖ TSSOP和SSOP 均为SOP衍生出来的封装。TSSOP的中文解释为&#xff1a;薄的缩小型 SOP封装。SSO…

GSVA,GSEA,KEGG,GO学习

目录 GSVA 1&#xff1a;获取注释基因集 2&#xff1a;运行 GSEA 1,示例数据集 2,运行 GSEA_KEGG富集分析 GSEA_GO富集分析 DO数据库GSEA MSigDB数据库选取GSEA KEGG 1&#xff1a;运行 2&#xff1a;绘图 bar图 气泡图 绘图美化 GO GSVA 1&#xff1a;获取注…

springBoot 配置druid多数据源 MySQL+SQLSERVER

1:pom 文件引入数据 <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.0</version> </dependency>…

mysql服务器数据同步

在Linux和Windows之间实现MySQL服务器数据的同步。下面是一些常见的方法和工具&#xff1a; 复制&#xff08;Replication&#xff09;&#xff1a;MySQL复制是一种常见的数据同步技术&#xff0c;可用于将一个MySQL服务器的数据复制到其他服务器。您可以设置主服务器&#xff…

CMSIS-RTOS在stm32使用

目录&#xff1a; 一、安装和配置CMSIS_RTOS.1.打开KEIL工程&#xff0c;点击MANAGE RUN-TIME Environment图标。2.勾选CMSIS CORE和RTX.3.配置RTOS 时钟频率、任务栈大小和数量&#xff0c; 软件定时器. 二、CMSIS_RTOS内核启动和创建线程。1.包含头文件。2.内核初始化和启动。…

C#,数值计算——插值和外推,曲线插值(Curve_interp)的计算方法与源程序

1 文本格式 using System; namespace Legalsoft.Truffer { /// <summary> /// Object for interpolating a curve specified by n points in dim dimensions. /// </summary> public class Curve_interp { private int dim { get; s…

openGauss通过VIP实现的故障转移

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是【IT邦德】&#xff0c;江湖人称jeames007&#xff0c;10余年DBA及大数据工作经验 一位上进心十足的【大数据领域博主】&#xff01;&#x1f61c;&am…

VisualGDB 6.0 R2 Crack

轻松跨平台"VisualGDB 使 Visual Studio 的跨平台开发变得简单、舒适。它支持&#xff1a; 准系统嵌入式系统和物联网模块&#xff08;查看完整列表&#xff09; C/C Linux 应用程序 本机 Android 应用程序和库 Raspberry Pi 和其他Linux 板 Linux 内核模块&#xff08;单…

【PTA题目】6-13 求叠数(递归版) 分数 10

6-13 求叠数(递归版) 分数 10 全屏浏览题目 切换布局 作者 李祥 单位 湖北经济学院 请编写递归函数&#xff0c;生成叠数。 例如&#xff1a;Redup(5,8)88888 函数原型 long long Redup(int n, int d); 说明&#xff1a;参数 n 为重复次数(非负整数)&#xff0c;d 为数字…

未来科技中的云计算之路

随着科技的不断发展&#xff0c;云计算已经不再是一个陌生的词汇&#xff0c;而是我们日常生活中不可或缺的一部分。从智能家居到无人驾驶&#xff0c;再到虚拟现实和人工智能&#xff0c;云计算在这些领域都扮演着至关重要的角色。在这篇博客中&#xff0c;我们将一同探索云计…

【如何学习Python自动化测试】—— 页面元素定位

接上篇自动化测试环境搭建&#xff0c;现在我们介绍 webdriver 对浏览器操作的 API。 2、 页面元素定位 通过自动化操作 web 页面&#xff0c;首先要解决的问题就是定位到要操作的对象&#xff0c;比如要模拟用户在页面上的输入框中输入一段字符串&#xff0c;那就必须得定位到…

UiPath Studio 2023.10 Crack

UiPath Studio是一款功能强大且用户友好的集成开发环境 (IDE)&#xff0c;专为机器人流程自动化 (RPA) 设计。它由自动化技术领域的领先公司UiPath开发。 以下是 UiPath Studio 的一些主要功能和组件&#xff1a; 图形用户界面 (GUI)&#xff1a;UiPath Studio 具有直观且用户友…

RT-Thread STM32F407 BMI088--SPI

BMI088是一款高性能6轴惯性传感器&#xff0c;由16位数字三轴24g加速度计和16位数字三轴2000/ s陀螺仪组成。 这里用SPI来驱动BMI088进行数据解读 第一步&#xff0c;首先在 RT-Thread Settings中进行配置 第二步&#xff0c;退出RT-Thread Settings&#xff0c;进入board.h…

数模建模竞赛——写作手三天速成(文末领取)

目录 第一天&#xff1a;准备论文模板&#xff0c;学习各类基础画图技巧 1、论文模板 2、基础画图能力 第二天&#xff1a;看按模型算法分类的优秀论文&#xff0c;学习其模型的写作方式 第三天&#xff1a;配合团队完成真题练习 第一天&#xff1a;准备论文模板&#xff…

【网络通信】探索UDP与TCP协议、IP地址和端口号的奥妙

&#x1f33a;个人主页&#xff1a;Dawn黎明开始 &#x1f380;系列专栏&#xff1a;网络奇幻之旅 ⭐每日一句&#xff1a;往前走&#xff0c;朝着光 &#x1f4e2;欢迎大家&#xff1a;关注&#x1f50d;点赞&#x1f44d;评论&#x1f4dd;收藏⭐️ 文章目录 &#x1f4cb;前…

嵌入式 Linux 移植与系统启动方法

1、Linux系统启动与U-Boot 所谓移植就是把程序代码从一种运行环境转移到另一种运行环境。对于内核移植来说&#xff0c;主要是从一种硬件平台转移到另一种硬件平台上运行。 体系结构级别的移植是指在不同体系结构平台上Linux内核的移植&#xff0c;例如&#xff0c;在ARM、MI…

【2023春李宏毅机器学习】生成式学习的两种策略

文章目录 1 各个击破2 一步到位3 两种策略的对比 生成式学习的两种策略&#xff1a;各个击破、一步到位 对于文本生成&#xff1a;把每一个生成的元素称为token&#xff0c;中文当中token指的是字&#xff0c;英文中的token指的是word piece。比如对于unbreakable&#xff0c;他…