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

news2025/1/17 17:54:49

目录

一 概念

二 运算符重载的实现

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

四 默认赋值运算符

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

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

相关文章

【使用vscode在线web搭建开发环境--code-server搭建】

官方版本下载 https://github.com/coder/code-server/releases?q4.0.0&expandedtrue使用大于版本3.8.0,因为旧版本有插件市场不能访问的情况版本太高需要更新环境依赖 拉取安装包 []# wget "https://github.com/coder/code-server/releases/download/v4.0.0/code-…

[游戏开发][Untiy]跨平台可视化Log系统

工具介绍 今天介绍的主角是LogViewer 工具运行时长这个样子&#xff0c;Unity的Log日志都会在这里显示 如何安装 在Unity商店搜索Log&#xff0c;排名第一的就是它 也可以去Github官网下载源码&#xff1a; Unity-Logs-Viewerhttps://github.com/aliessmael/Unity-Logs-Vie…

机器学习的医疗乳腺癌数据的乳腺癌疾病预测

项目视频讲解:基于机器学习的医疗乳腺癌数据的乳腺癌疾病预测 完整代码数据分享_哔哩哔哩_bilibili 效果演示: 代码: #第一步!导入我们需要的工具 import numpy as np import pandas as pd import matplotlib.pyplot as plt import seaborn as sns %matplotlib inlin…

Python的数据分析包Pandas?示例文章完成版来啦~

文章目录 前言一、Pandas简介二、Python Pandas的使用 总结 前言 pandas 是基于NumPy 的一种工具&#xff0c;该工具是为了解决数据分析任务而创建的。 Pandas 是python的一个数据分析包&#xff0c;最初由AQR Capital Management于2008年4月开发&#xff0c;并于2009年底开源…

Javaweb之Vue指令案例的详细解析

2.3.5 案例 需求&#xff1a; 如上图所示&#xff0c;我们提供好了数据模型&#xff0c;users是数组集合&#xff0c;提供了多个用户信息。然后我们需要将数据以表格的形式&#xff0c;展示到页面上&#xff0c;其中&#xff0c;性别需要转换成中文男女&#xff0c;等级需要…

Consistency Models 阅读笔记

Diffusion models需要多步迭代采样才能生成一张图片&#xff0c;这导致生成速度很慢。Consistency models的提出是为了加速生成过程。 Consistency models可以直接一步采样就生成图片&#xff0c;但是也允许进行多步采样来提高生成的质量。 Consistency models可以从预训练的扩…

制作Go程序的Docker容器

今天突然遇到需要将 Go 程序制作成 Docker 的需求&#xff0c;所以进行了一些研究。方法很简单&#xff0c;但是官方文档和教程有些需要注意的地方&#xff0c;所以写本文进行记录。 源程序 首先介绍一下示例程序&#xff0c;示例程序是一个 HTTP 服务器&#xff0c;会显示si…

Docker安装Zookeeper

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

教你轻松解决win系统ucrtbased.dll丢失的问题,亲测有效!

ucrtbased.dll是一个动态链接库文件&#xff08;DLL&#xff09;&#xff0c;它是Windows操作系统中的一部分&#xff0c;主要负责提供操作系统和应用程序所需的函数和接口。这个文件包含了操作系统和应用程序共同使用的通用代码&#xff0c;以确保不同程序之间的兼容性和稳定性…

webstorm/idea配置leetcode刷题

File -> settings -> Plugins -> 搜索leetcode 安装插件&#xff08;截图显示我已经安装过了&#xff09;&#xff0c;安装完成后点击OK操作&#xff0c;在编辑器四个边角就会出现一个leetcode的插件 File -> settings -> Tools-> Leetcode plugin 点击…

CorelDRAW2024最新版本的图形设计软件

CorelDRAW2024是Corel公司推出的最新版本的图形设计软件。CorelDRAW是一款功能强大的矢量图形编辑工具&#xff0c;被广泛用于图形设计、插图、页面布局、照片编辑和网页设计等领域。 1. 新增的设计工具&#xff1a;CorelDRAW 2024引入了一些全新的设计工具&#xff0c;使用户能…

[Kettle] 字段处理

1.增加常量 常量是指在计算机程序运行过程中其值不能改变的量 常量可以是任何的数据类型&#xff0c;例如&#xff0c;圆周率"3.141159"、中国首都"北京"等都可以是常量 增加常量是指在数据中增加一个字段&#xff0c;并给字段设置一个固定的值 数据源…

4.2 Windows驱动开发:内核中进程线程与模块

内核进程线程和模块是操作系统内核中非常重要的概念。它们是操作系统的核心部分&#xff0c;用于管理系统资源和处理系统请求。在驱动安全开发中&#xff0c;理解内核进程线程和模块的概念对于编写安全的内核驱动程序至关重要。 内核进程是在操作系统内核中运行的程序。每个进…

zyj-ha 安装过程及使用部署

一&#xff0e;安装过程排坑 1. 硬件环境准备 排坑 1 首先&#xff0c;服务器至少需要 2 台&#xff0c;每台服务器至少需要 2 块网卡&#xff0c;并且必须有预留 心跳线网口&#xff0c;不能被其他业务占用&#xff0c;否则容易出现脑裂。 2. 通过配置管理工具导入安装包 …

【南京】最新ChatGPT/GPT4科研技术应用与AI绘图及论文高效写作

2023年我们进入了AI2.0时代。微软创始人比尔盖茨称ChatGPT的出现有着重大历史意义&#xff0c;不亚于互联网和个人电脑的问世。360创始人周鸿祎认为未来各行各业如果不能搭上这班车&#xff0c;就有可能被淘汰在这个数字化时代&#xff0c;如何能高效地处理文本、文献查阅、PPT…

大师学SwiftUI第18章Part1 - 图片选择器和相机

如今&#xff0c;个人设备主要用于处理图片、视频和声音&#xff0c;苹果的设备也不例外。SwiftUI可以通过​​Image​​视图显示图片&#xff0c;但需要其它框架的支持来处理图片、在屏幕上展示视频或是播放声音。本章中我们将展示Apple所提供的这类工具。 图片选择器 Swift…

OpenCV入门6——图像基本变换

文章目录 图像的放大与缩小缩放算法放大 图像的翻转图像的旋转仿射变换之图像平移仿射变换之获取变换矩阵仿射变换之变换矩阵之二OpenCV透视变换 图像的放大与缩小 缩放算法 # -*- coding: utf-8 -*- import cv2 import numpy as npimg cv2.imread(E://pic//4.jpg) # (600, 48…

搭建 AI 图像生成器 (SAAS) php laravel

今天来搭一套&#xff0c;AI 图像生成器 是基于 Openai DALLE 2 和 Openai DALLE 3 以及 Stability AI 和稳定扩散 API 构建的脚本&#xff0c;为用户提供了使用简单的提示和大小生成独特自定义图像的可能性。在这个平台上&#xff0c;创意得以快速、高效地实现&#xff0c;借助…

继承语法详解

继承语法详解 一:继承1&#xff1a;什么是继承 二&#xff1a;访问成员变量三&#xff1a;访问成员方法四&#xff1a;访问父类的成员变量和成员方法super关键字super和this关键字的区别 五&#xff1a;子类的构造方法六&#xff1a;代码块七&#xff1a;final关键字八&#xf…

驱动程序无法通过使用安全套接字层(SSL)加密与 SQL Server 建立安全连接

参考&#xff1a;https://www.cnblogs.com/sam-snow-v/p/15917898.html eclipse链接SQL Server出现问题 笔者使用Open JDK 17&#xff0c;SQL Server 2016&#xff0c;项目中使用JPA操作数据库。测试环境没问题&#xff0c;生产环境出现如题所示“驱动程序无法通过使用安全套接…