目录
日期类:
运算符重载:
编辑
赋值重载:
拷贝构造和赋值重载的区别:
实现赋值重载:
划分成员函数:
日期类的声明和定义分离
日期类-=:
日期类-
前置后置++
日期类:
写一个简单的日期类:
#pragma once;
class Date
{
public:
	Date(int year=1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};#include<iostream>
using namespace std;
#include"Date.h"
void TestDate()
{
	Date d1;
	Date d2(2022, 9, 18);
	Date d3(2022, 2, 30);
	Date d4(2022, 2, 49);
	d3.Print();
	d4.Print();
}
int main()
{
	TestDate();
	return 0;
}我们发现,日期d3和d4都不合法,我们进行运行:

也没有提示我们输入日期错误。
我们可以对我们的构造函数进行优化:
Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
		if (!(year >= 1 && month >= 1 && month <= 12 && day >= 1 && day <= GetMonthDay(year, month)))
		{
			cout << "非法日期" << endl;
		}
	}日期类的拷贝构造:
对于日期类,我们不需要写对应的拷贝构造,因为对于内置类型,编译器会默认执行值拷贝:
void TestDate()
{
	Date d1;
	Date d2(2022, 9, 18);
	/*Date d3(2022, 2, 30);
	Date d4(2022, 2, 49);
	d3.Print();
	d4.Print();*/
	Date d5(d2);
	d5.Print();
}
int main()
{
	TestDate();
	return 0;
}
运算符重载:
赋值重载:
void TestDate()
{
	Date d1;
	Date d2(2022, 9, 18);
	/*Date d3(2022, 2, 30);
	Date d4(2022, 2, 49);
	d3.Print();
	d4.Print();*/
	/*Date d5(d2);
	d5.Print();*/
	Date d5;
	d5 = d2;
}
拷贝构造和赋值重载的区别:
拷贝构造:一个对象拷贝初始化另一个要创建的对象。
赋值重载:已经存在的两个对象之间的拷贝。

实现赋值重载:

赋值重载可以传值传参吗?
可以,不会发生无穷递归,发生无穷递归的条件是: 拷贝构造时,使用传值传参,传值传参本身就是拷贝构造,拷贝构造又需要传值传参,无限循环。
所以这里可以传值传参。
但是传值传参还需要调用拷贝构造,有些麻烦,我们最好还是使用传地址传参。

void operator=(const Date&d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}void TestDate()
{
	Date d1(2022, 9, 10);
	Date d2;
	d2 = d1;
}
int main()
{
	TestDate();
	return 0;
}我们进行测试:

赋值成功。
赋值重载写的不够全面,我们知道整型可以连等:
int main()
{
	/*TestDate();*/
	int i, j;
	i = j = 1;
	return 0;
}我们这里是不能连等的:
void TestDate()
{
	Date d1(2022, 9, 10);
	Date d2, d3;
	d2.Print();
	d3=d2 = d1;
	d2.Print();
}
int main()
{
	TestDate();
	/*int i, j;
	i = j = 1;*/
	return 0;
}
原因如下:
 
 
这里我们把d1赋值给d2之后,返回值应该是d2,但是我们实现的函数是没有返回值的,所以我们需要完善返回值:
Date operator=(const Date&d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
		return *this;
	}传值传参是拷贝构造,传值返回也是拷贝构造,所以我们这里可以用传引用返回:
而且this指向的对象出了作用域依旧存在,我们可以用引用返回。
能不能不返回*this,返回d呢?
答:不可以
Date& operator=(const Date&d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
		return d;
	}1:因为d是只读的,传引用返回后的类型变成可读可写的了,属于权限的放大。
2:赋值的返回值是左操作数,不能返回右操作数。
划分成员函数:

赋值运算符的重载对于内置类型完成值拷贝,所以对于日期类,我们可以不定义赋值重载
 
我们没有定义赋值重载函数,依旧可以完成日期类对象的赋值。
大部分类的默认赋值重载都不需要我们自定义,但是有些需要,例如栈:
#pragma once;
class Stack
{
public:
	Stack(int capacity=4 )
	{
		_a = (int*)malloc(sizeof(int)*capacity);
		if (_a == nullptr)
		{
			perror("malloc fail");
			exit(-1);
		}
		_top = 0;
		_capacity = capacity;
		cout << "Stack构造函数()" << endl;
	}
	Stack(const Stack&st)
	{
		_a=(int*)malloc(sizeof(int)*st._capacity);
		if (_a == nullptr)
		{
			perror("malloc fail");
			exit(-1);
		}
		memcpy(_a, st._a, sizeof(int)*st._top);
		_top = st._top;
		_capacity = st._capacity;
	}
	void Push(int x)
	{
		_a[_top++] = x;
	}
	~Stack()
	{
		free(_a);
		_a = nullptr;
		_top = _capacity = 0;
		cout << "Stack析构函数()" << endl; 
	}
private:
	int*_a;
	int _capacity;
	int _top;
};我们没有实现赋值重载,看看能否使用赋值重载
void StackTest()
{
	Stack st1;
	st1.Push(1);
	st1.Push(2);
	Stack st2;
	st2.Push(10);
	st2.Push(20);
	st2.Push(30);
	st2.Push(40);
	st1 = st2;
}
int main()
{
	StackTest();
	/*int i, j;
	i = j = 1;*/
	return 0;
}
运行之后,直接报错。
 
我们进行值拷贝:
把st2._top赋值给st1._top,把st2._capacity赋值给st1._capacity。
再让st1._a指向st2._a的位置。

后创建st2的先析构,析构之后st2所指向的空间的数据释放了,这时候st1再进行析构就导致了越界访问。
并且原本属于st1的指向的数据的地址也找不到了,造成了内存泄漏。

所以对于栈类,我们要自己实现赋值重载:
 
我们先释放掉st1所指向的空间的数据,然后新创建一个空间,拷贝st2的数据,让st1指向该空间:
 
Stack& operator=(const Stack&st)
	{
		free(_a);
		_a = (int*)malloc(sizeof(int)*st._capacity);
		if (_a == nullptr)
		{
			perror("malloc fail");
			exit(-1);
		}
		memcpy(_a, st._a, sizeof(int)*st._top);
		_top = st._top;
		_capacity = st._capacity;
		return *this;
	} 
完成了深拷贝。
还有一种情况:

用自己去赋值自己。
 
我们进行运行,发现数据出现了错误。
 因为
我们可以先提前判断:
Stack& operator=(const Stack&st)
	{
		if (&st != this)
		{
			free(_a);
			_a = (int*)malloc(sizeof(int)*st._capacity);
			if (_a == nullptr)
			{
				perror("malloc fail");
				exit(-1);
			}
			memcpy(_a, st._a, sizeof(int)*st._top);
			_top = st._top;
			_capacity = st._capacity;
		}
		return *this;
	}对于一些有资源释放的类,我们也可以不自己写:
class MyQueue
{
public:
	void push(int x)
	{
		_pushST.Push(x);
	}
private:
	Stack _pushST;
	Stack _popST;
	size_t size = 0;
};对于内置类型完成值拷贝,对于自定义类型,调用其默认赋值重载。
void MyQueueTest()
{
	MyQueue s1;
	s1.push(10);
	MyQueue s2;
	s2 = s1;
}
int main()
{
	MyQueueTest();
	/*int i, j;
	i = j = 1;*/
	return 0;
}
完成了深拷贝。
日期类的声明和定义分离
对于比较小并且频繁调用的函数,这类函数一般是内联函数,内联函数只能在类里面定义,不能声明和定义分离。
对于行数不多并且调用不频繁的我们可以声明和定义分离。
日期类-=:
Date&Date::operator-=(int day)
{
	_day -= day;
	while (_day <= 0)
	{
		_month--;
		if (_month == 0)
		{
			_year--;
		}
		_day += GetMonthDay(_year, _month);
	}
	return *this;
}#include"Date.h"
void TestDate1()
{
	Date d1(2022, 10, 19);
	d1 -= 1000;
	d1.Print();
}
int main()
{
	TestDate1();
	return 0;
}
日期类-
Date Date::operator-(int day)
{
	Date ret(*this);
	ret -=day;
	return ret;
}#include"Date.h"
void TestDate1()
{
	Date d1(2022, 10, 19);
	Date d2 = d1 - 10000;
	d2.Print();
}
int main()
{
	TestDate1();
	return 0;
}
假如我们要减去一个负数呢?
#include"Date.h"
void TestDate1()
{
	Date d1(2022, 10, 19);
	
	d1 -= -10000;
	d1.Print();
}
int main()
{
	TestDate1();
	return 0;
}前置后置++

 
 
我们在定义的时候如何区分前置和后置++?
 
Date& Date::operator++()
{
	*this += 1;
	return *this;
}
Date Date::operator++(int)
{
	Date ret = *this;
	*this + 1;
	return ret;
} 




















