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

news2024/9/28 3:27:29

目录

一 概念

二 运算符重载的实现

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

四 默认赋值运算符

五 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.coloradmin.cn/o/1223099.html

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

相关文章

实验六:Android的网络编程基础

实验六&#xff1a;Android 的网络编程基础 6.1 实验目的 本次实验的目的是让大家熟悉 Android 开发中的如何获取天气预报&#xff0c;包括了 解和熟悉 WebView、WebService 使用、网络编程事件处理等内容。 6.2 实验要求 熟悉和掌握 WebView 使用 了解 Android 的网络编程…

cs与msf联动

实验环境 cs4.4(4.5版本不知道为啥实现不了) cs服务器与msf在同一台vps上 本地win7虚拟机 cs派生会话给msf 首先cs正常上线win7&#xff0c;这就不多说了&#xff0c;然后说如何将会话派生给msf cs准备 选择Foreign&#xff0c;这里可以选HTTP&#xff0c;也可以选HTTPS…

LLM大模型权重量化实战

大型语言模型 (LLM) 以其广泛的计算要求而闻名。 通常&#xff0c;模型的大小是通过将参数数量&#xff08;大小&#xff09;乘以这些值的精度&#xff08;数据类型&#xff09;来计算的。 然而&#xff0c;为了节省内存&#xff0c;可以通过称为量化的过程使用较低精度的数据类…

庖丁解牛:NIO核心概念与机制详解

文章目录 Pre输入/输出Why NIO流与块的比较通道和缓冲区概述什么是缓冲区&#xff1f;缓冲区类型什么是通道&#xff1f;通道类型 NIO 中的读和写概述Demo : 从文件中读取1. 从FileInputStream中获取Channel2. 创建ByteBuffer缓冲区3. 将数据从Channle读取到Buffer中 Demo : 写…

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

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

JVM:字节码文件,类的生命周期,类加载器

JVM&#xff1a;字节码文件&#xff0c;类的生命周期&#xff0c;类加载器 为什么要学这门课程 1. 初识JVM1.1. 什么是JVM1.2. JVM的功能1.3. 常见的JVM 2. 字节码文件详解2.1. Java虚拟机的组成2.2. 字节码文件的组成2.2.1. 以正确的姿势打开文…

电脑软件:推荐一款非常实用的固态硬盘优化工具

目录 一、软件简介 二、工作原理 三、功能介绍 3.1、优化SSD设置 3.2、查看驱动器信息 3.3、查看SMART数据 3.4、停用Windows事件日志记录 3.5、禁用Windows碎片整理 3.6、时间戳停用 3.7、禁用引导文件的碎片整理 3.8、关闭短名称 四、使用教程 4.1 安装说明 4.…

OpenCV图像处理、计算机视觉实战应用

OpenCV图像处理、计算机视觉实战应用 专栏简介一、基于差异模型模板匹配缺陷检测二、基于NCC多角度多目标匹配三、基于zxing多二维码识别四、基于tesseract OCR字符识别 专栏简介 基于OpenCV C分享一些图像处理、计算机视觉实战项目。不定期持续更新&#xff0c;干货满满&…

[C/C++]数据结构 链表(单向链表,双向链表)

前言: 上一文中我们介绍了顺序表的特点及实现,但是顺序表由于每次扩容都是呈二倍增长(扩容大小是自己定义的),可能会造成空间的大量浪费,但是链表却可以解决这个问题. 概念及结构: 链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接…

【自用总结】正项级数审敛法的总结

注&#xff1a;收敛半径的求法就是lim n->∞ |an1/an| ρ&#xff0c;而ρ1/R&#xff0c;最基本的不能忘。 比较判别法&#xff1a;从某项起&#xff0c;该级数后面的项均小于等于另一级数&#xff0c;则敛散性可进行一定的比较 可以看到&#xff0c;比较判别法实际上比较…

跟我一起来做一个音视频产品功能!

前言&#xff1a; 大家好&#xff0c;上来和大家汇报一下h264翻译进度&#xff0c;目前翻译完了第六章&#xff0c;第七章快翻译完了&#xff0c;马上可以翻译第八章。 在第七章翻译完了之后&#xff0c;我会做一个知识点总结出来&#xff0c;一起学习&#xff0c;一起进步&…

Android 13.0 Launcher3仿ios长按app图标实现抖动动画开始拖拽停止动画

1.概述 在13.0的系统rom定制化开发中,在对系统原生Launcher3的定制需求中,也有好多功能定制的,在ios等电子产品中 的一些好用的功能,也是可以被拿来借用的,所以在最近的产品开发需求中,需求要求模仿ios的 功能实现长按app图标实现抖动动画,接下来看如何分析该功能的实现…

【开源】基于JAVA的校园失物招领管理系统

项目编号&#xff1a; S 006 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S006&#xff0c;文末获取源码。} 项目编号&#xff1a;S006&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、研究内容2.1 招领管理模块2.2 寻物管理模块2.3 系…

主键问题以及分布式 id

分布式 id 需要处理的问题主要是同一时间在多台机器中保证生成的 id 唯一&#xff0c;为了这么做我们可以这么做&#xff1a; 分布式 id 生成策略 先说几个已经被淘汰的策略引出分布式 id 的问题 1&#xff0c;UUID&#xff1a;UUID 随机并且唯一&#xff0c;在单一的数据库…

调整COSWriter解决X-easypdf / PDFBOX生成大量数据时OOM问题

背景 业务需要生成一个15W数据左右的PDF交易报表。希望我们写在一个文件里&#xff0c;不拆分成多个PDF文件。 使用的技术组件 <dependency><groupId>wiki.xsx</groupId><artifactId>x-easypdf-pdfbox</artifactId><version>2.11.10<…

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

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

IPSecGRE

IPSec&GRE 手工方式建立IPSec隧道组网实验拓扑配置步骤第一步配置IP地址第二步配置静态路由第三步配置IPSec 抓包测试 GRE Over IPSec功能的配置组网实验拓扑配置命令 配置GRE使用静态路由组网图实验拓扑配置步骤1.配置RouterA2.配置RouterB3.配置RouterC4.验证配置结果 手…

【限时免费】20天拿下华为OD笔试之 【前缀和】2023B-最大子矩阵和【欧弟算法】全网注释最详细分类最全的华为OD真题题解

文章目录 题目描述与示例题目描述输入描述输出描述示例输入输出说明 解题思路如何表示一个子矩阵暴力解法二维前缀和优化二维前缀和矩阵的构建 代码解法一&#xff1a;二维前缀和PythonJavaC时空复杂度 解法二&#xff1a;暴力解法&#xff08;不推荐&#xff09;PythonJavaC时…

Pattern Recognition投稿经验

文章目录 ManuscriptTitle PageHighlightsAuthor BiographyDeclarationSubmit 合作推广&#xff0c;分享一个人工智能学习网站。计划系统性学习的同学可以了解下&#xff0c;点击助力博主脱贫( •̀ ω •́ )✧ 停更了大半年&#xff0c;近期终于完成了论文投稿&#xff0c;趁…

聚观早报 |联想集团Q2财季业绩;小鹏汽车Q3营收

【聚观365】11月17日消息 联想集团Q2财季业绩 小鹏汽车Q3营收 微软发布两款自研AI芯片 FAA批准SpaceX再次发射星际飞船 2023 OPPO开发者大会 联想集团Q2财季业绩 全球数字经济领导企业联想集团公布截至2023年9月30日的2023/24财年第二财季业绩&#xff1a;整体营收达到10…