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

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


五、赋值运算符重载

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 …… &
这两个一般不用重新定义,编译器会默认生成,如果有别的用途,可以显式定义重载


今日分享结束~

在这里插入图片描述

相关推荐

  1. C++对象()

    2024-07-11 18:04:01       67 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-11 18:04:01       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-11 18:04:01       106 阅读
  3. 在Django里面运行非项目文件

    2024-07-11 18:04:01       87 阅读
  4. Python语言-面向对象

    2024-07-11 18:04:01       97 阅读

热门阅读

  1. 7.10飞书一面面经

    2024-07-11 18:04:01       27 阅读
  2. mysql bit 对gorm使用何种类型?

    2024-07-11 18:04:01       31 阅读
  3. python爬虫学习(三十三天)---多线程上篇

    2024-07-11 18:04:01       27 阅读
  4. 一、Python 日志系统设计之不同级别的系统日志

    2024-07-11 18:04:01       26 阅读
  5. SpringAMQP收发消息demo

    2024-07-11 18:04:01       23 阅读
  6. SpringSecurity中文文档(Servlet OAuth 2.0 Login)

    2024-07-11 18:04:01       22 阅读