前文提要
本文全文以日期类为例
 先简单写一个日期类
class Data{
public:
private:
	int _year;
	int _month;
	int _day;
};
且补充一个小知识
数据类型的划分
内置类型:是编程语言提供的基本数据类型,例如整数、浮点数、字符、布尔值
 自定义类型:是开发人员根据应用程序的需求定义的类型,它可以由内置类型组成,也可以由其他自定义类型组成。自定义类型可以通过类、结构体、枚举等方式进行定义。
默认成员函数
默认成员函数是一些 当我们什么都没有编写时,也会自动在类中生成的函数
构造函数
是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,以保证每个数据成
 员都有 一个合适的初始值,并且在对象整个生命周期内只调用一次。
特征
- 函数名与类名相同。
- 无返回值。
- 对象实例化时编译器自动调用对应的构造函数。
由上述三条可知构造函数的基本特质
 先写一个简单的构造函数
	//函数名与类名相同 且 无返回值
	Date()
	{
		_year = 0;
		_month = 0;
		_day = 0;
	}
- 构造函数可以重载。
构造函数可以支持重载的化,便可以支持各种各样的初始化
// 1.无参构造函数
 Date()
 {}
 // 2.带参构造函数
 Date(int year, int month, int day)
 {
 _year = year;
 _month = month;
 _day = day;
 }
 //全缺省的构造
  Date(int year = 0, int month = 0, int day = 0)
 {
 _year = year;
 _month = month;
 _day = day;
 }
 //半缺省的构造
  Date(int year, int month = 0, int day = 0)
 {
 _year = year;
 _month = month;
 _day = day;
 }
- 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成
- 无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。
 由于构造函数是对象实例化时编译器自动调用
 虽然无参的构造函数与全缺省的构造函数满足函数重载的要求。
 但不满足构造函数的要求,全缺省与无参的函数会引起歧义
 当函数调用不传参数时,编译器无法识别调用哪个构造函数
  
默认构造函数
是一个在创建对象时被自动调用的特殊的构造函数。它通常没有任何参数,并且不执行任何特定的初始化操作。默认构造函数的作用是初始化对象的成员变量,确保对象在创建时处于一个合理的状态。
无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认构造函数。
默认构造函数由编译器自动生成的构造函数,是一个无参的构造函数
 构造函数中对于成员对象的处理根据成员对象的类型分为两类
 内置类型不处理
 自定义类型调用自定义类型的默认构造函数
 也因此,建议每个函数都提供默认构造函数。
 此外,因内置类型不予处理,c++也时常被人诟病
 在C++11 中针对内置类型成员不初始化的缺陷,又打了补丁,即:内置类型成员变量在类中声明时可以给默认值。
故而,整体上我们日期类的构造函数
Date(int year = 1900, int month = 1, int day = 1)
	// 拷贝构造函数Date::Date(int year , int month, int day)
{
	_year = year;
	_month = month;
	_day = day;
}
析构函数
与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。
特征
- 析构函数名是在类名前加上字符 ~。
- 无参数无返回值类型。
- 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重
 载
- 对象生命周期结束时,C++编译系统系统自动调用析构函数。
- 如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如Date类;
- 有资源申请时,一定要写,否则会造成资源泄漏,比如Stack类
回到我们的例子来
 Date没有申请空间,析构函数是可以不写的
拷贝构造
只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。
特征
- 拷贝构造函数是构造函数的一个重载形式。
- 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用。
按定义可知,拷贝构造中有且只含有一个参数->类类型对象
 如果直接传类类型对象本身,传值的过程本身就是一次类对象的拷贝
 也不可避免的会引起无穷递归调用
-  若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。 
 类中如果没有涉及资源申请时,拷贝构造函数是否写都可以;一旦涉及到资源申请时,则拷贝构造函数是一定要写的
-  拷贝构造函数典型调用场景: 
- 使用已存在对象创建新对象
- 函数参数类型为类类型对象
- 函数返回值类型为类类型对象
回到例子上来
 日期类没有设计资源申请,默认的拷贝函数也足以满足需求
赋值重载
赋值重载也可以是一种特殊的默认成员函数,不手动书写时,会在类中自动生成。具体在操作符重载中细说
运算符重载
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数。也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
原型
函数名字为:关键字operator后面接需要重载的运算符符号。
 函数原型:返回值类型 operator操作符(参数列表)
注意
- 不能通过连接其他符号来创建新的操作符:比如operator@
- 重载操作符必须有一个类类型参数
- 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义
- 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this
- .* :: sizeof ?: .注意以上5个运算符不能重载。这个经常在笔试选择题中出现。
赋值运算符的重载
格式
参数类型:const T&,传递引用可以提高传参效率
 返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
 检测是否自己给自己赋值
 返回*this :要复合连续赋值的含义
特征
- 赋值运算符只能重载成类的成员函数不能重载成全局函数
赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的
 赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了
2.用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝 (与默认拷贝构造类似)
回到例子上来,与拷贝构造同理
前置++ 与 后置++
前置++和后置++都是一元运算符
 为了让前置++与后置++形成能正确重载
C++规定:后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器自动传递
注意:后置++是先使用后+1,因此需要返回+1之前的旧值,故需在实现时需要先将this保存一份,然后给this+1
 以日期类未例
 	// 前置++
	Date& operator++()
	{
		return (*this) += 1;
	}
	// 后置++
	Date operator++(int)
	{
		Date tmp = *this;
		*this += 1;
		return tmp;
	}
日期类的实现
Date.cpp
#include"Data.h"
// 获取某年某月的天数
int Date::GetMonthDay(int year, int month)
{
	static 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 % 400 == 0))
	{
		day++;
	}
	return day;
}
// 全缺省的构造函数
Date::Date(int year , int month, int day)
{
	_year = year;
	_month = month;
	_day = day;
}
// d2(d1)
Date::Date(const Date& d)
{
	_year = d._year;
	_month = d._month;
	_day = d._day;
}
Date.h
#pragma once
#include<iostream>
using namespace std;
class Date
{
public:
	// 获取某年某月的天数
	int GetMonthDay(int year, int month);
	// 全缺省的构造函数
	Date(int year = 1900, int month = 1, int day = 1);
	// 拷贝构造函数
  // d2(d1)
	Date(const Date& d);
	// 赋值运算符重载
  // d2 = d3 -> d2.operator=(&d2, d3)
	Date& operator=(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
		return *this;
	}
	// 析构函数
	~Date()
	{
	};
	// 日期+=天数
	Date& operator+=(int day)
	{
		_day += day;
		while (_day > GetMonthDay(_year, _month))
		{
			_day -= GetMonthDay(_year, _month);
			_month++;
			if (_month == 13)
			{
				_month = 1;
				_year++;
			}
		}
		return *this;
	}
	void days()
	{
		int day = _day;
		for (int i = 1; i < _month; i++)
		{
			day += GetMonthDay(_year, i);
		}
		cout << "侯稼澍" << _year << ":" << _month << ":" << _day << "为第" << day << "天" << endl;
	}
	void print()
	{
		cout << _year << " " << _month << " " << _day << endl;
	}
	// 日期+天数
	Date operator+(int day)
	{
		Date tmp = *this;
		return tmp += day;
	}
	// 日期-天数
	Date operator-(int day)
	{
		Date tmp = *this;
		return tmp -= day;
	}
	// 日期-=天数
	Date& operator-=(int day)
	{
		_day -= day;
		while (_day <= 0)
		{
			_month--;
			if (_month == 0)
			{
				_month = 12;
				_year--;
			}
			_day += GetMonthDay(_year, _month);
		}
		return *this;
	}
	// 前置++
	Date& operator++()
	{
		return (*this) += 1;
	}
	// 后置++
	Date operator++(int)
	{
		Date tmp = *this;
		*this += 1;
		return tmp;
	}
	// 后置--
	Date operator--(int)
	{
		Date tmp = *this;
		*this -= 1;
		return tmp;
	}
	// 前置--
	Date& operator--()
	{
		return *this -= 1;
	}
	// >运算符重载
	bool operator>(const Date& d)
	{
		if ((_year > d._year)
			|| (_year == d._year && _month > d._month)
			|| (_year == d._year && _month == d._month && _day > d._day))
			return true;
		else
			return false;
	}
	// ==运算符重载
	bool operator==(const Date& d)
	{
		return ((_year == d._year) && (_month == d._month) && (_day == d._day));
	}
	// >=运算符重载
	bool operator >= (const Date& d)
	{
		return (*this > d) || (*this == d);
	}
	// <运算符重载
	bool operator < (const Date& d)
	{
		return !(*this >= d);
	}
	// <=运算符重载
	bool operator <= (const Date& d)
	{
		return (*this < d) || (*this == d);
	}
	// !=运算符重载
	bool operator != (const Date& d)
	{
		return !(*this == d);
	}
	// 日期-日期 返回天数
	int operator-(const Date& d)
	{
		int count = 0;
		Date big = *this;
		Date small = d;
		if (small > big)
		{
			small = *this;
			big = d;
		}
		while (big != small)
		{
			small++;
			count++;
		}
		return count;
	}
private:
	int _year;
	int _month;
	int _day;
};



















