「C++」类和对象(3)

news2024/11/15 17:15:32

欢迎大家再次来到海盗猫鸥的博客——

今天将继续讲解类和对象的后续内容,本篇将讲解类和对象中运算符重载,赋值运算符重载,以及取地址运算符的内容,再结合内容进行Date日期类的实现。

目录

运算符重载

运算符重载

赋值运算符重载

取地址运算符重载

const成员函数

取地址运算符重载

Date日期类的实现

Date.h头文件:

Date.cpp文件

补充

结语


运算符重载

当运算符被用于非内置类型时,可能将导致报错。而C++允许通过运算符重载的形式来指定运算符新的含义,从而达到处理自定义类型的功能。

运算符重载

  1. 运算符重载实际就是拥有特殊名字的函数,由operator加上需要重定义的运算符共同构成;和普通的函数一样,拥有返回值,参数和函数体。
  2. 重载运算符函数的参数个数和该运算符的操作对象数量相同,即一元运算符只有一个参数,二元运算符有两个参数,二元运算符的左操作数传给第一个参数,有操作数传给第二个参数。
  3. 如果重载运算符函数是一个成员函数,那么他的第一个运算对象默认传递给隐式的this指针,所以运算符重载作为成员函数时,参数会比运算符操作数少一个。
  4. 运算符重载不改变运算符的优先级和结合性。
  5. 只能重载语法中与原本就存在的运算符,不能用来创建新的运算符。如:operator@
  6. .*    ::    sizeof   ? :   . 这五个运算符不能重载
  7. 重载运算符至少有一个自定义类型的参数,运算符对内置类型的运算规则,无法通过重载来改变
  8. 一个类需要重载哪些运算符,是看哪些运算符重载后对于这个类有意义
  9. 重载自增++运算符时,由于有前置和后置的区别,但运算符重载时函数名都为operator++,为了区分,在后置++重载时,添加一个int形参(该参数不接收内容,只用于区分),由此来和前置++的构成重载函数。
  10. 重载流插入<<和流提取>>时,需要重载为全局函数,因为成员函数的第一个参数默认为类对象的this指针,此时调用函数,就变成了 对象<<cout 或者 对象 >> cin ;不符合使用习惯,所以床在为全局函数,将istream/ostream放到第一个参数位置,将对象放到第二个参数位置。
//类类型流输出
ostream& Date::operator<<(ostream& out)
{
	//定义为成员函数,第一个参数位置将被this指针占用,导致重载<<的左右操作数相对于原本的参数位置颠倒
	out << _year << "年" << _month << "月" << _day << "日" << endl;

	return out;
}
ostream& operator<<(ostream& out, const Date& d)
{
	//全局函数不能访问类私有成员,需要进行友元声明
	out << d._year << "年" << d._month << "月" << d._day << "日" << endl;

	return out;
}

赋值运算符重载

赋值运算符重载是一个默认成员函数,当没有显示定义时,编译器器会自动生成,注意和拷贝构造区分。

  1. 赋值运算符重载必须重载为成员函数,参数建议写为const的类类型引用,减少拷贝;
  2. 返回值为左操作数,即第一个参数,使用引用返回提高效率;
  3. 未显示定义时编译器会自动生成默认赋值运算符重载,和默认拷贝构造函数相似,只会对内置类型进行浅拷贝/值拷贝,自定义类型则调用它的赋值重载。
  4. 类中没有资源的使用时,默认的重载函数就可以满足要求,当的类的成员变量有指向资源时,就需要显示定义其功能。

取地址运算符重载

const成员函数

  1. 将const修饰的成员函数称为const成员函数,const放在函数参数列表的后面

    	void Print() const;
    	bool operator<(const Date& d) const;
    	bool operator<=(const Date& d) const;
    	bool operator>(const Date& d) const;
    	bool operator>=(const Date& d) const;
    	bool operator==(const Date& d) const;
    	bool operator!=(const Date& d) const;
    
  1. const实际修饰的时隐含的this指针,使该this指针指向的对象在该函数体内不可修改,Date类为例原本this指针为:Date* const this,而const成员函数的this为:const Date* const this;
  2. 当成员函数不涉及修改this指针的对象时,都建议设置为const成员函数,提高安全性。

取地址运算符重载

取地址运算符重载有普通取地址运算符重载和const取地址运算符重载,但一般由编译器默认生成的就能满足需求。

Date* operator&()
{
return this;
// return nullptr;
}
const Date* operator&()const
{
return this;
// return nullptr;
}

Date日期类的实现

Date.h头文件:

由于成员变量都为内置类型的变量,所以拷贝构造以及析构函数都可以不写;

GetMonthDay函数用于获取当年当月的天数,调用频繁且函数简短,设计为inline内联函数效率更高,此处自动为inline内联函数。

#pragma once
#include <iostream>
using std::cout;
using std::cin;
using std::endl; 

class Date
{
	//声明友元函数,使全局函数也能使用类私有成员
	friend ostream& operator<<(ostream& out, const Date& d);
	friend istream& operator>>(istream& in, Date& d);
public:
	//全缺省构造函数
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;

		if (!CheckDate())
		{
			cout << "日期错误" << endl;
			Print();
		}
	}

	//获取月份天数(常调用的简短函数设计为inline函数)
	int GetMonthDay(int year, int month) const
	{
		//定义为静态数组,防止每次调用都重新创建数组导致效率降低
		static int MonthDayArray[13] = { -1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
		//闰年二月
		if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))//先判断是否为2月,可以提高效率
		{
			return 29;
		}
		return MonthDayArray[month];
	}

	void Print() const
	{
		cout << _year << "年" << _month << "月" << _day << "日" << endl;
	}

	//判断日期是否正确
	bool CheckDate() const;

	Date& operator=(const Date& d);

	Date operator+(int day) const;
	Date operator-(int day) const;
	Date& operator+=(int day);
	Date& operator-=(int day);

	bool operator<(const Date& d) const;
	bool operator<=(const Date& d) const;
	bool operator>(const Date& d) const;
	bool operator>=(const Date& d) const;
	bool operator==(const Date& d) const;
	bool operator!=(const Date& d) const;

	//前置++
	Date& operator++();
	//后置++,添加一个int形参以示区分,没有实际作用
	Date operator++(int);
	//前置--
	Date& operator--();
	//后置--
	Date operator--(int);

	//日期 - 日期 = 天数
	int operator-(const Date& d) const;

	//ostream& operator<<(ostream& out);

	//取地址运算符重载
	/*Date* operator&();
	const Date* operator&() const;*/

private:
	int _year;
	int _month;
	int _day;
};

Date.cpp文件

用于实现各个运算符重载函数;

注意函数间可以进行复用,从而简洁的实现相关的函数,减少不必要的重复代码书写。

#include "Date.h"
bool Date::CheckDate() const
{
	if ((_month < 1 || _month > 12) || (_day < 1 || _day > GetMonthDay(_year, _month)))
	{
		return false;
	}
	return true;
}

Date& Date::operator=(const Date& d)
{
	if (this != &d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	
	//返回对象为左操作数
	return *this;
}

Date Date::operator+(int day) const
{
	Date tmp = *this;
	//tmp._day += day;
	//while (tmp._day > GetMonthDay(tmp._year, tmp._month))
	//{
	//	tmp._day -= GetMonthDay(tmp._year, tmp._month);
	//	tmp._month++;
	//	if (tmp._month == 13)
	//	{
	//		tmp._year++;
	//		tmp._month = 1;
	//	}
	//}
	tmp += day;
	return tmp;
}
Date Date::operator-(int day) const
{
	Date tmp = *this;
	//tmp._day -= day;
	//while (tmp._day <= 0)
	//{
	//	//
	//	tmp._month--;
	//	if (tmp._month == 0)
	//	{
	//		tmp._year--;
	//		tmp._month = 12;
	//	}
	//	tmp._day += GetMonthDay(tmp._year, tmp._month);
	//	
	//}
	tmp -= day;
	return tmp;
}
Date& Date::operator+=(int day)
{
	if (day < 0)
	{
		*this -= (-day);
		return *this;
	}
	_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)
{
	//*this = *this - day//复用operator-,但效率相对低
	if (day < 0)
	{
		*this += (-day);
		return *this;
	}
	_day -= day;
	while (_day <= 0)
	{
		//
		_month--;
		if (_month == 0)
		{
			_year--;
			_month = 12;
		}
		_day += GetMonthDay(_year, _month);

	}
	return *this;
}


bool Date::operator<(const Date& d) const
{
	if (_year < d._year)
	{
		return true;
	}
	else if (_year == d._year)
	{
		if (_month < d._month)
		{
			return true;
		}
		else if (_month == d._month)
		{
			if (_day < d._day)
			{
				return true;
			}
			//return _day < d._day;
		}
	}
	return false;
}
bool Date::operator<=(const Date& d) const
{
	//if (_year <= d._year && _month <= d._month && _day <= d._day)
	//{
	//	return true;
	//}
	//return false;
	return *this < d || *this == d;
}
bool Date::operator>(const Date& d) const
{
	return !(*this <= d);
}
bool Date::operator>=(const Date& d) const
{
	return !(*this < d);
}
bool Date::operator==(const Date& d) const
{
	//if (d._year == _year && d._month == _month && d._day == _day)
	//{
	//	return true;
	//}
	//return false;
	return d._year == _year 
		&& d._month == _month 
		&& d._day == _day;
}
bool Date::operator!=(const Date& d) const
{
	//if (d._year == _year && d._month == _month && d._day == _day)
	//{
	//	return false;
	//}
	//return true;
	return !(*this == d);
}

//前置++
Date& Date::operator++()
{
	*this += 1;
	//返回++之后的值
	return *this;
}
//后置++,添加一个int形参以示区分,没有实际作用
Date Date::operator++(int)
{
	Date tmp = *this;
	*this += 1;
	//返回++之前的值
	return tmp;
}

//前置--
Date& Date::operator--()
{
	*this -= 1;
	return *this;
}
//后置--
Date Date::operator--(int)
{
	Date tmp = *this;
	*this += 1;
	return tmp;
}
//日期相减
int Date::operator-(const Date& d) const
{
	int n = 0;
	int flag = 1;
	Date max = *this;
	Date min = d;
	if (*this < d)
	{
		max = d;
		min = *this;
		flag = -1;
	}
	while (min != max)
	{
		++min;
		++n;
	}
	return n * flag;
}

//类类型流输出
//ostream& Date::operator<<(ostream& out)
//{
//	//定义为成员函数,第一个参数位置将被this指针占用,导致重载<<的左右操作数相对于原本的参数位置颠倒
//	out << _year << "年" << _month << "月" << _day << "日" << endl;
//
//	return out;
//}
ostream& operator<<(ostream& out, const Date& d)
{
	//全局函数不能访问类私有成员,需要进行友元声明
	out << d._year << "年" << d._month << "月" << d._day << "日" << endl;

	return out;
}

istream& operator>>(istream& in, Date& d)
{
	while (1)
	{
		cout << "请输入日期>" << endl;
		in >> d._year >> d._month >> d._day;
		if (!d.CheckDate())
		{
			cout << "非法日期,请重新输入:" << endl;
			in >> d._year >> d._month >> d._day;
		}
		else
		{
			break;
		}
	}
	return in;
}

//Date* Date::operator&()
//{
//	return this;
//}
//const Date* Date::operator&() const
//{
//	return this;
//}

注意:在使用自增和自减时,在保证正确性的前提下建议尽量使用前置自增和自减,因为在重载自增和自减函数时,前置自增自减没有拷贝的过程,效率更高。

补充

cout为ostream类型的对象,cin是istream类型的对象,内置类型之所以能直接使用<<流插入和>>流提取,就是因为默认已经重载好了相关的函数,所以我们重载时原理时相似的。

以流插入<<为例:

结语

本篇博客到此结束,感谢大家的阅读,下篇将是类和对象的最后一篇博客——

个人主页:海盗猫鸥-CSDN博客

本期专栏:C++_海盗猫鸥的博客-CSDN博客

感谢大家关注~

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

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

相关文章

【CMake】使用CMake在Visual Stdudio编译资源文件和多目标编译

一、资源文件的编译 首先&#xff0c;我们的项目结构如下&#xff0c;存在图片和第三方库&#xff1a; 配置主 C M a k e l i s t s CMakelists CMakelists&#xff1a; #需求的最低cmake程序版本 cmake_minimum_required(VERSION 3.12)#本工程的名字 project(OpenGL)#支持的…

[Python数据可视化] Plotly:交互式数据可视化的强大工具

引言&#xff1a; 在数据分析和可视化的世界中&#xff0c;Plotly 是一颗耀眼的明星。它是一个开源的交互式图表库&#xff0c;支持多种编程语言&#xff0c;包括 Python、R 和 JavaScript。Plotly 的强大之处在于它能够创建出既美观又具有高度交互性的图表&#xff0c;使得数据…

变压器数据集,电气设备红外热图像数据集,部分带温度显示。变压器红外数据集,温度显示5000多张

项目背景&#xff1a; 变压器作为电力系统的关键设备之一&#xff0c;其运行状态直接影响到电网的安全稳定。红外热成像技术因其非接触、快速、直观的特点&#xff0c;在电力设备的故障诊断和状态监测中得到了广泛应用。本数据集旨在为基于红外热图像的变压器状态监测提供高质量…

挑战力扣高难度算法、数据库题

一.算法类 1622题,困难,奇妙序列 class Fancy { public:static const int MOD 1e9 7;long long M_total; // cumulative multiplicative factorlong long A_total; // cumulative additive factorvector<long long> val; // original valuesvector<long long> …

AtCoder Beginner Contest 371

A - Jiro &#xff1a; 题目&#xff1a; 代码&#xff1a; #include <bits/stdc.h>using namespace std;typedef long long LL ; typedef pair<int,int> PII;void solve() {string a,b, c;cin>>a>>b>>c;string s(3,a);s[0]a[0];s[1]b[0];s[2…

MATLAB窗口操作常用命令

MATLAB窗口操作常用命令 命令功能clc清除窗口命令clear commandclf清除图形对象(窗口)clear清除工作区所有变量 释放内存clear all清除工作区的所有变量和函数type显示指定文件的所有内容与CMD命令类似dir查看当前工作文件夹中的内容与CMD命令类似save保存工作区或工作区中任何…

C语言-结构体-详解

博客主页&#xff1a;【夜泉_ly】 本文专栏&#xff1a;【C语言】 欢迎点赞&#x1f44d;收藏⭐关注❤️ C语言-结构体-详解 1.前言2.结构体类型2.1声明2.2变量的创建与初始化2.3访问2.4匿名结构体类型 3.结构体内存对齐3.1对齐规则3.2示例 1.前言 在C语言中&#xff0c;除了整…

java -- JDBC

一.JDBC概述: 过java语言操作数据库中的数据。 1.JDBC概念 JDBC&#xff08;Java DataBase Connectivity,java数据库连接&#xff09;是一种用于 执行SQL语句的Java API。JDBC是Java访问数据库的标准规范&#xff0c;可以 为不同的关系型数据库提供统一访问&#xff0c;它由…

Mathematics(未完成)

点击字母M有惊喜 /*Mathematics */ #include <stdlib.h> #include <stdio.h> #include <conio.h> #include <easyx.h> #include <math.h> #define L 4 #define H 80 #define T 0.3141592653589793 #define T0 10 #define PI 3.1415926535897932 …

fiddler抓包01:工具介绍

课程大纲 fiddler是一款常见的抓包工具&#xff0c;可以对web端和移动端的接口请求进行抓包&#xff08;截获&#xff09;、分析、编辑、模拟等&#xff0c;还可以导出jmeter、Loadrunner测试脚本。 1、原理 fiddler作为代理服务器&#xff0c;拦截请求和服务器响应。 2、使用…

图像放大的软件PhotoZoom 9新功能介绍及安装激活使用指南

最近&#xff0c;全世界大量巨头企业都在人工智能领域投入大笔的科研经费&#xff0c;而和有关人工智能的话题也一直吸引着人们的眼球。像“无人驾驶”、“智能交互”、“物联网”等新兴行业&#xff0c;也对人工智能有着迫切的需求。 AI人工智能影响了社会的方方面面。 之前有…

sqlgun靶场攻略

步骤一&#xff1a;打开页面 步骤二&#xff1a;测试回显点 -1union select 1,2,3# 步骤三&#xff1a;查看数据库名 -1union select 1,2,database()# 步骤四&#xff1a;查看表名 -1union select 1,2,group_concat(table_name) from information_schema.tables where table…

【IP协议】IP协议报头结构

文章目录 IP 协议报头结构4位版本4位首部长度8位服务类型16位总长度16位标识、3位标志、13位片偏移8位生存时间8位协议16位首部校验和32源 IP 地址、32位目的 IP 地址 IP 协议报头结构 4位版本 实际上只有两个取值 4 > IPv4&#xff08;主流&#xff09;6 > IPv6 IPv2&…

electron-updater实现electron全量版本更新

在 Electron 应用中使用 electron-updater 来实现自动更新功能时&#xff0c;通常你会在一个专门的模块或文件中管理更新逻辑。如果你想要使用 ES6 的 import 语法来引入 electron-updater&#xff0c;你需要确保你的项目已经配置好了支持 ES6 模块的构建工具&#xff08;如 We…

MiniBlogum项目简介

MiniBlogum项目简介 文章目录 MiniBlogum项目简介一、引言二、技术栈与开发环境三、主要功能&#xff08;一&#xff09;用户注册与登录&#xff08;二&#xff09;查看当前登录用户/作者头像、昵称、Gitee仓库地址&#xff08;三&#xff09;查看博客列表&#xff08;四&#…

HAL库STM32常用外设教程(四)—— 定时器 基本定时

HAL库STM32常用外设教程&#xff08;四&#xff09;—— 定时器 基本定时 文章目录 HAL库STM32常用外设教程&#xff08;四&#xff09;—— 定时器 基本定时前言一、定时器特性概述二、基础定时器的结构和功能1、基本特征2、基础定时器相关寄存器3、基础定时器工作流程4、基础…

基于python+django+vue的家居全屋定制系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【2025最新】基于协同过滤pythondjangovue…

文件误删除后的数据救援实战指南

在数字化时代&#xff0c;文件误删除成为了许多用户心头挥之不去的阴影。无论是手误点击了“删除”键&#xff0c;还是系统崩溃导致的数据丢失&#xff0c;文件一旦从我们的视线中消失&#xff0c;往往伴随着重要信息的流失和工作的中断。本文将深入探讨文件误删除的现象&#…

kubernetes技术详解,带你深入了解k8s

目录 一、Kubernetes简介 1.1 容器编排应用 1.2 Kubernetes简介 1.3 k8s的设计架构 1.3.1 k8s各个组件的用途 1.3.2 k8s各组件之间的调用关系 1.3.3 k8s的常用名词概念 1.3.4 k8s的分层结构 二、k8s集群环境搭建 2.1 k8s中容器的管理方式 2.2 k8s环境部署 2.2.1 禁用…

如何在GitHub上克隆仓库:HTTPS、SSH和GitHub CLI的区别

GitHub是开发者的天堂&#xff0c;提供了丰富的工具和功能来管理代码和项目。在克隆GitHub仓库时&#xff0c;你可能会遇到三种常见的方法&#xff1a;HTTPS、SSH和GitHub CLI。每种方法都有其独特的优势和适用场景。本文将深入探讨这三种克隆方式的区别&#xff0c;帮助你选择…