【C++】类和对象(中)--下篇

news2025/3/1 6:43:17

在这里插入图片描述
个人主页~
类和对象上
类和对象中-上篇


类和对象

  • 五、赋值运算符重载
    • 1、运算符重载
    • 2、赋值运算符重载
    • 3、前置++和后置++重载
  • 六、const成员
  • 七、日期类的实现
    • Date.h
    • Date.cpp
    • test.cpp
      • test1测试结果
      • test2测试结果
      • test3测试结果
      • test4测试结果
      • test5测试结果
      • test6测试结果
      • test7测试结果
  • 八、关于取地址和const取地址操作符重载

五、赋值运算符重载

1、运算符重载

运算符重载是具有特殊函数名的函数,是C++为了增强代码可读性而引入的

operator sign(parameter);

operator为关键字,sign就是需要重载的运算符符号,parameter为参数(可以为多个)

注意事项:

不能通过连接其他符号来创建新的操作符

重载操作符至少有一个类类型参数

用于内置类型之间的运算符含义不改变,编译器会自动检测使用运算符的类型是什么,从而选择是否改变运算符含义

类成员函数重载时有一个隐藏的参数this

不能重载的五个运算符
.*
::
sizeof
?:
.

重载一个==

//……全局
bool operator==(const Date& d1, const Date& d2)
{
    return d1._year == d2._year
   && d1._month == d2._month
        && d1._day == d2._day;
}
//……

但定义一个全局函数需要成员函数公有,所以我们直接定义在类内,保证其封装性

//……类内
bool operator==(const Date& d2)
{
	return _year == d2._year
		&& _month == d2._month
		&& _day == d2._day;
}
//……

2、赋值运算符重载

(1)格式
参数类型 const name& 引用传参

返回值类型 name& 返回引用

检测是否自己给自己赋值

返回*this

(2)赋值运算符的重载
赋值运算符只能重载成类的成员函数不能重载成全局函数

赋值运算符如果不显式实现,编译器会生成一个默认的,此时再在外边实现一个全局的赋值运算符重载,就会发生冲突

class Date
{
public:
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	Date(const Date & d)
	{ 
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	Date& operator=(const Date& d)
	{
		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		return *this;
	}

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

(3)用户没有显式实现时,编译器会生成一个默认赋值运算符重载,然后值拷贝,内置成员直接赋值,自定义成员需要调用对应类的赋值运算符重载完成赋值

(4)有了值拷贝我们就一定要说说深拷贝,在Date类这样的类中不需要我们自己实现,而在Stack这样的类中就需要显式实现,进行资源管理

拿出我们的老演员栈:

typedef int DataType;
class Stack
{
public:
	Stack(size_t capacity = 10)
	{
		arr = (DataType*)malloc(capacity * sizeof(DataType));
		if (nullptr == arr)
		{
			perror("malloc申请空间失败");
			return;
		}
		size = 0;
		capacity = capacity;
	}
	void Push(const DataType& data)
	{
		// CheckCapacity();
		arr[size] = data;
		size++;
	}
	~Stack()
	{
		if (arr)
		{
			free(arr);
			arr = nullptr;
			capacity = 0;
			size = 0;
		}
	}
private:
	DataType* arr;
	size_t size;
	size_t capacity;
};
int main()
{
	Stack s1;
	s1.Push(1);
	s1.Push(2);
	Stack s2;
	s2 = s1;
	return 0;
}

在这里插入图片描述
调试后发现又是析构函数这里出了问题,原因同拷贝构造函数:因为编译器自动生成的拷贝构造函数是值拷贝,所以在生成s2时,s2中的指针a指向的数组与s1中的指针指向的数组相同,在程序结束时,调用析构函数释放了s2,对应的这块数组空间也被释放,然后调用析构函数释放s1,已经被释放的空间不能被再次释放,所以出现了这样的错误,所以我们需要自己显式定义一个拷贝构造函数

3、前置++和后置++重载

我们先来复习一下前置++和后置++的区别,在仅自加时也就是在n++为一条语句时没有区别,在赋值时,前置++是先+1后赋值,后置++是先赋值再+1

如果我们想要++重载,那么就是定义operator++,分不出为前置还是后置,所以我们规定operator++为前置++operator++(int)为后置++

class Date
{
public:
	Date(int year = 1970, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	Date& operator++()
	{
		*this += 1;
		return *this;
	}
	Date operator++(int)
	{
		Date tmp(*this);
		*this += 1;
		return tmp;
	}
	//所以后置++会使用空间拷贝,效率比前置++低
	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d;
	Date d1(2000,1,1);
	d = d1++;
	d.Print();
	d1.Print();
	d = ++d1;
	d.Print();
	d1.Print();
	return 0;
}

在这里插入图片描述

六、const成员

被const修饰的成员函数称之为const成员函数,const实际修饰其中隐含的this指针,表明在该成员函数中不能对类内的任何成员进行修改

因为参数为隐藏的,所以我们的方法如下:

void Date::Print() const
{
	cout << _year << "/" << _month << "/" << _day << endl;
}

相当于

void Date::Print(const Date* this) 
{
	cout << _year << "/" << _month << "/" << _day << endl;
}

注意:

const对象不能调用非const成员函数

非const对象能调用const成员函数

const成员函数内不能调用其他非const成员函数

非const成员函数内能调用其他const成员函数

七、日期类的实现

Date.h

#pragma once
#include <iostream>
using namespace std;

class Date
{
public:
	int GetMonthDay(int year, int month);

	Date(int year = 1970, int month = 1, int day = 1);
	Date(const Date& d);
	~Date();

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

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

	int operator-(const Date& d);

	void Print() const;
private:
	int _year;
	int _month;
	int _day;
};

Date.cpp

#define _CRT_SECURE_NO_WARNINGS

#include "Date.h"

int Date::GetMonthDay(int year, int month)
{
	const static int days[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
	if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
	{
		return 29;
	}
	return days[month];
}

Date::Date(int year, int month, int day)
{
	_year = year;
	_month = month;
	_day = day;
	if (month < 1 || month > 12
		|| day < 1 || day > GetMonthDay(year, month))
	{
		cout << "非法日期" << endl;
		exit(-1);
	}
}

Date::Date(const Date& d)
{
	_year = d._year;
	_month = d._month;
	_day = d._day;
}

Date::~Date()
{
	_year = _month = _day = 0;
}
//日期类的析构函数其实没必要写
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)
{
	if (day < 0)
	{
		return *this -= (-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)
{
	if (day < 0)
	{
		return *this += (-day);
	}//复用+=

	_day -= day;

	while (_day <= 0)
	{
		--_month;
		if (_month == 0)
		{
			--_year;
			_month = 12;
		}

		_day += GetMonthDay(_year, _month);
	}

	return *this;
}

Date& Date::operator++()
{
	*this += 1;
	return *this;
}

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

Date Date::operator+(int day)
{
	Date tmp(*this);

	tmp += day;

	return tmp;
}

Date Date::operator-(int day)
{
	Date tmp(*this);

	tmp -= day;

	return tmp;
}

bool Date::operator==(const Date& d)
{
	return _year == d._year
		&& _month == d._month
		&& _day == d._day;
}

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

// d1 <= d2
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);
}

bool Date::operator!=(const Date& d)
{
	return !(*this == d);
}

int Date::operator-(const Date& d)
{
//假设左大右小,此时标识符flag为1
	Date max = *this;
	Date min = d;
	int flag = 1;
//如果左小右大,则置换后置标识符flag为-1
	if (*this < d)
	{
		max = d;
		min = *this;
		flag = -1;
	}
//算出大的与小的之间的天数
	int n = 0;
	while (min != max)
	{
		++min;
		++n;
	}
//返回与标识符flag的乘积
	return n * flag;
}

void Date::Print() const
{
	cout << _year << "/" << _month << "/" << _day << endl;
}

test.cpp

#define _CRT_SECURE_NO_WARNINGS

#include "Date.h"
//初始化
void test1()
{
	Date d1(2000, 1, 1);
	d1.Print();
	Date d2;
	d2.Print();
	Date d3 = Date(d1);
	d3.Print();
}
//++
void test2()
{
	Date d1(2001, 2, 28);
	Date d2 = d1++;
	Date d3 = ++d1;
}
//+ -
void test3()
{
	Date d1(2000, 1, 1);
	Date d2 = d1 + 20000;
	Date d3 = d1 - 20000;
}
//--
void test4()
{
	Date d1(2000, 3, 1);
	Date d2 = d1--;
	Date d3 = --d1;
}
//+= -=
void test5()
{
	Date d1(2000, 1, 1);
	d1 += 20000;
	Date d2;
	d2 -= 20000;
}
//日期-日期
void test6()
{
	Date d1(2000, 1, 1);
	Date d2;
	int a = d1 - d2;
}
//=
void test7()
{
	Date d1(2000, 1, 1);
	Date d2;
	d2 = d1;
}
int main()
{
	//test1();
	//test2();
	//test3();
	//test4();
	//test5();
	//test6();
	//test7();
	return 0;
}

test1测试结果

在这里插入图片描述
构造和拷贝构造函数正常

test2测试结果

在这里插入图片描述
在这里插入图片描述
前置后置++都正常

test3测试结果

在这里插入图片描述

在这里插入图片描述

+、 - 不改变原来的值,正常

test4测试结果

在这里插入图片描述

在这里插入图片描述
前置后置- -正常

test5测试结果

在这里插入图片描述
-= +=改变原来的数,正常

test6测试结果

在这里插入图片描述
日期减日期为整数正常

test7测试结果

在这里插入图片描述

=赋值正常

全部正常

八、关于取地址和const取地址操作符重载

&
const …… &
这两个一般不用重新定义,编译器会默认生成,如果有别的用途,可以显式定义重载


今日分享结束~

在这里插入图片描述

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

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

相关文章

【Dison夏令营 Day 12】如何用 Python 构建数独游戏

通过本综合教程&#xff0c;学习如何使用 Pygame 在 Python 中创建自己的数独游戏。本指南涵盖安装、游戏逻辑、用户界面和计时器功能&#xff0c;是希望创建功能性和可扩展性数独益智游戏的爱好者的理想之选。 数独是一种经典的数字谜题&#xff0c;多年来一直吸引着谜题爱好…

【Elasticsearch】Elasticsearch倒排索引详解

文章目录 &#x1f4d1;引言一、倒排索引简介二、倒排索引的基本结构三、Elasticsearch中的倒排索引3.1 索引和文档3.2 创建倒排索引3.3 倒排索引的存储结构3.4 词典和倒排列表的优化 四、倒排索引的查询过程4.1 过程4.2 示例 五、倒排索引的优缺点5.1 优点5.2 缺点 六、倒排索…

阶段三:项目开发---搭建项目前后端系统基础架构:任务9:导入空管基础数据

任务描述 本阶段任务是导入项目的基础数据&#xff0c;包括空管基础数据和离线的实时飞行数据&#xff08;已经脱敏&#xff09;。 任务指导 本阶段任务需要导入两种数据&#xff1a; 1、在MySQL中导入空管基础数据 kongguan.sql空管基础数据表说明&#xff1a; 1告警信息…

什么牌子的头戴式蓝牙耳机好性价比高?

说起性价比高的头戴式蓝牙耳机,就不得不提倍思H1s,作为倍思最新推出的新款,在各项功能上都实现了不错的升级,二字开头的价格,配置却毫不含糊, 倍思H1s的音质表现堪称一流。它采用了40mm天然生物纤维振膜,这种振膜柔韧而有弹性,能够显著提升低音的量感。无论是深沉的低音还是清…

C语言 | Leetcode C语言题解之第221题最大正方形

题目&#xff1a; 题解&#xff1a; int maximalSquare(char** matrix, int matrixSize, int* matrixColSize){int dp[301][301]{0};int wid0;if(matrixSize0&&matrixColSize[0]0){return 0;}for(int i0;i<matrixSize;i){for(int j0;j<matrixColSize[0];j){if(m…

linux docker部署(离线.deb)非桌面版

目录 一.查看本机系统 二.下载deb包 三.安装 一.查看本机系统 uname -m二.下载deb包 下载官网&#xff1a;Index of linux/ubuntu/dists/focal/pool/stable/amd64/ (docker.com) 目录说明&#xff1a; 我是ubuntu20.04&#xff0c;选择focal&#xff1a; edge/&#xff…

R语言实战—圆形树状图

话不多说&#xff0c;先看最终效果&#xff1a; 圆形树状图是树状图的一个变型&#xff0c;其实都是层次聚类。 接下来看代码步骤&#xff1a; 首先要先安装两个包&#xff1a; install.packages("ggtree") install.packages("readxl") 咱就别问问什么…

群体优化算法----化学反应优化算法介绍,解决蛋白质-配体对接问题示例

介绍 化学反应优化算法&#xff08;Chemical Reaction Optimization, CRO&#xff09;是一种新兴的基于自然现象的元启发式算法&#xff0c;受化学反应过程中分子碰撞和反应机制的启发而设计。CRO算法模拟了分子在化学反应过程中通过能量转换和分子间相互作用来寻找稳定结构的…

C++ unique_ptr智能指针学习

unique_ptr是一种定义在<memory>中的智能指针(smart pointer)。它持有对对象的独有权——两个unique_ptr不能指向一个对象&#xff0c;不能进行复制操作只能进行移动操作。 如下图&#xff0c;定义p1为unique_ptr类型指针&#xff0c;如果把p1赋给p2&#xff0c;则编译出…

【题解】—— LeetCode一周小结27

&#x1f31f;欢迎来到 我的博客 —— 探索技术的无限可能&#xff01; &#x1f31f;博客的简介&#xff08;文章目录&#xff09; 【题解】—— 每日一道题目栏 上接&#xff1a;【题解】—— LeetCode一周小结26 2024.7 1.最大化一张图中的路径价值 题目链接&#xff1a;…

墨烯的C语言技术栈-C语言基础-008

八.转义字符(转变原来字符的意思) 假如我们要在屏幕上打印一个目录:C:\code\test.c 我们应该如何写代码 int main() { printf("abc\n") // \让n转变 换行作用 return 0; } imt main() { printf("abc\0def") // \0后就是结束了后面打不印了 return 0; }…

C语言指针,的声明、取地址、*解引用、设置值、偏移、自增 的问题举例

C语言指针,的声明、&取地址、※解引用、设置值、偏移、自增 的问题举例代码&#xff1a;int※a,d[]{1,2,4,8};char*f,e[]“Hellow…”;a&b; P1※a; ※a4; P1※(a);f&e[7]; &#xff08;备注&#xff1a;※代表*&#xff0c;转义问题&#xff0c;没显示&#xff09…

Spring Cloud: Nacos配置中心与注册中心的使用

一、配置中心(配置管理) 配置中心是一种集中化管理配置的服务。它的主要作用包括集中管理配置信息&#xff0c;将不同服务的配置信息集中存储和管理&#xff1b;支持动态更新配置&#xff0c;通过操作界面或 API 无需重启服务即可应用最新配置信息&#xff1b;实现配置信息共享…

通过AIS实现船舶追踪与照射

前些天突然接到个紧急的项目&#xff1a;某处需要实现对夜航船只进行追踪并用激光灯照射以保障夜航安全。这个项目紧急到什么程度呢&#xff1f;&#xff01;现场激光灯都安装好了&#xff0c;还有三个星期就要验收了&#xff0c;但上家没搞定就甩给我们了:( 从技术上看&#…

【Python】已解决:xml.parsers.expat.ExpatError: no element found: Line 1, column 0

文章目录 一、分析问题背景二、可能出错的原因三、错误代码示例四、正确代码示例五、注意事项 已解决&#xff1a;xml.parsers.expat.ExpatError: no element found: Line 1, column 0 一、分析问题背景 在使用Python的xml.parsers.expat模块解析XML文件时&#xff0c;有时会…

咬文嚼字:词元是当今生成式人工智能失败的一个重要原因

生成式人工智能模型处理文本的方式与人类不同。了解它们基于"标记"的内部环境可能有助于解释它们的一些奇怪行为和顽固的局限性。从 Gemma 这样的小型设备上模型到 OpenAI 业界领先的 GPT-4o 模型&#xff0c;大多数模型都建立在一种称为转换器的架构上。由于转换器在…

Java中读写文件内容乱码/BufferedReader读文件内容乱码/OutputStreamWriter设置编码集

1、问题概述&#xff1f; 在项目中我们经常会将例如日志信息放入到txt(任意后缀)文档中&#xff0c;然后在项目中通过弹框等形式查看直接的查看这些文档中的信息&#xff0c;然后有时候会出现乱码的情况。 这个时候我们就需要设置写入和写出时候的编码集情况。 2、解决方案 …

【PB案例学习笔记】-29制作一个调用帮助文档的小功能

写在前面 这是PB案例学习笔记系列文章的第29篇&#xff0c;该系列文章适合具有一定PB基础的读者。 通过一个个由浅入深的编程实战案例学习&#xff0c;提高编程技巧&#xff0c;以保证小伙伴们能应付公司的各种开发需求。 文章中设计到的源码&#xff0c;小凡都上传到了gite…

Windows使用nxlog发送系统日志到Linux的rsyslog服务器

Windows使用nxlog发送系统日志到Linux的rsyslog服务器 前言一、IP地址规划及示意图二、在windows上安装及配置nxlog1.下载nxlog2.安装nxlog3.配置nxlog4.创建对应日志路径的文件夹 三、windows上启动nxlog服务四、在CentOS 7上配置日志存到指定位置文件1.编辑/etc/rsyslog.conf…