【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类型, 只不过两个都存在的时候, 会优先调用最匹配的一个

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

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

继续加油!

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

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

相关文章

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

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

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

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

【数据结构与算法】JavaScript实现双向链表

文章目录 一、双向链表简介二、封装双向链表类2.0.创建双向链表类2.1.append(element)2.2.toString()汇总2.3.insert(position,element)2.4.get(position)2.5.indexOf(element)2.7.update(position,element)2.8.removeAt(position)2.9.其他方法2.10.完整实现 三、链表结构总结3…

苍穹外卖--员工分页查询

请求参数封装&#xff1a; Data public class EmployeePageQueryDTO implements Serializable {//员工姓名private String name;//页码private int page;//每页显示记录数private int pageSize;}请求结果封装&#xff1a; public class PageResult implements Serializable {…

青岛数字孪生赋能工业制造,加速推进制造业数字化转型

随着企业数字化进程的推进&#xff0c;数字孪生技术逐渐在汽车行业得到广泛应用。5G与数字孪生、工业互联网的融合将加速数字中国、智慧社会建设&#xff0c;加速中国新型工业化进程&#xff0c;为中国经济发展注入新动能。数字孪生、工业物联网、工业互联网等新一代信息通信技…

Pattern Recognition投稿经验

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

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

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

什么是PWA(Progressive Web App)?它有哪些特点和优势?

聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…

【算法训练营】参数解析+跳石板

&#x1f308;欢迎来到Python专栏 &#x1f64b;&#x1f3fe;‍♀️作者介绍&#xff1a;前PLA队员 目前是一名普通本科大三的软件工程专业学生 &#x1f30f;IP坐标&#xff1a;湖北武汉 &#x1f349; 目前技术栈&#xff1a;C/C、Linux系统编程、计算机网络、数据结构、Mys…

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

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

小程序授权获取昵称

wxml: <form bindsubmit"formsubmit"><view style"width: 90%;display: flex;margin-left: 5%;"><view class"text1">昵称&#xff1a;</view><input style"width: 150px;margin-left: 30px;margin-top: 30px;…

操作系统秋招面试题

自己在秋招过程中遇到的高频操作系统相关的面试题 内存管理 虚拟内存 虚拟内存的⽬的是为了让物理内存扩充成更⼤的逻辑内存&#xff0c;从⽽让程序获得更多的可⽤内存。 为了更好的管理内存&#xff0c;操作系统将内存抽象成地址空间。每个程序拥有⾃⼰的地址空间&#xff…

【具身智能评估1】具身视觉语言规划(EVLP)仿真环境汇总

参考论文&#xff1a;Core Challenges in Embodied Vision-Language Planning 论文作者&#xff1a;Jonathan Francis, Nariaki Kitamura, Felix Labelle, Xiaopeng Lu, Ingrid Navarro, Jean Oh 论文原文&#xff1a;https://arxiv.org/abs/2106.13948 论文出处&#xff1a;Jo…

恕我直言,大模型对齐可能无法解决安全问题,我们都被表象误导了

是否听说过“伪对齐”这一概念&#xff1f; 在大型语言模型&#xff08;LLM&#xff09;的评估中&#xff0c;研究者发现了一个引人注目的现象&#xff1a;当面对多项选择题和开放式问题时&#xff0c;模型的表现存在显著差异。这一差异根源在于模型对复杂概念的理解不够全面&…

SpringCloud 之Feign的性能优化

Feign底层默认是JDK自带的HttpURLConnection&#xff0c;它是单线程发送HTTP请求的&#xff0c;不能配置线程池&#xff0c;我们使用Okhttp或者HttpClien 朵发送http请求&#xff0c;并且它们两个都支持线程池。 常见HTTP客户端 HttpClient HttpClient 是 Apache Jakarta Comm…

数位和相等数对的最大和【教3妹学编程-算法题】数位和相等数对的最大和

3妹&#xff1a;2哥&#xff0c;你有没有看到新闻“18岁父亲为4岁儿子落户现身亲子鉴定” 2哥 : 啥&#xff1f;18岁就当爹啦&#xff1f; 3妹&#xff1a;确切的说是14岁好吧。 2哥 : 哎&#xff0c;想我30了&#xff0c; 还是个单身狗。 3妹&#xff1a;别急啊&#xff0c; 2…

mfc140u.dll丢失的解决方法,以及针对每个解决mfc140u.dll丢失办法的优缺点

在使用电脑的过程中&#xff0c;有时会遇到一些与动态链接库文件&#xff08;DLL&#xff09;相关的错误。其中&#xff0c;mfc140u.dll丢失是一种常见的问题&#xff0c;它可能导致应用程序无法正常运行。在本文中&#xff0c;我们将探讨关于mfc140u.dll丢失的解决办法&#x…

PS学习笔记——初识PS界面

文章目录 PS界面 PS界面 我使用的是PS2021&#xff0c;可能不同版本界面有所不同&#xff0c;但大体来说没有太多差异 可以看到下面这个图就是ps的主界面&#xff0c;大体分为菜单栏、选项栏、工具栏、面板、以及最中央的工作区。 ps中的操作基本都能在菜单栏中找到 可以从菜…

(c语言进阶)内存函数

一.memcpy(void* dest,void* src,int num) &#xff0c;操作单位为字节&#xff0c;完成复制且粘贴字符串 1.应用 #include <stdio.h> #include<string.h> int main() {int arr1[] { 1,2,3,4,5,6,7,8,9,10 };int arr2[20] { 0 };memcpy(arr2, arr1, 20);//从…