C++ || 类的八种函数 | 默认成员函数 | 构造函数 | 析构函数 | 拷贝构造函数 | 赋值运算符重载 | const成员函数 | 取地址及const取地址操作符重载

默认成员函数

默认成员函数定义

当一个类中什么成员都没有时,简称为空类

空类中真的什么都没有吗?并不是,在任何类只什么都没有的话,编译器就会自动生成以下6种默认成员函数

  • 默认成员函数:用户没有显式实现,编译器自动生成的成员函数称为默认成员函数。

C++中,6个默认成员函数。

类中的成员函数,不仅仅局限于以下六种,仅仅只是以下六种,在用户没有显式实现的情况,编译器可以自动生成的成员函数。

默认成员函数分类

构造函数

构造函数定义

构造函数特殊的成员函数名字与类名相同创建类类型对象时由编译器自动调用,以保证类中的每个成员都有一个合适的初始值,并且在对象整个生命周期内只调用一次

简单来说,就是类似于初始化Init函数。

构造函数的价值,防止C语言中Init函数忘记。例如,尤其在栈中。

与Init函数,最本质的区别是:可以自动调用。

构造函数语法特性

构造函数特殊的成员函数

构造函数的主要任务并不是开空间创造对象,而是初始化对象

在创建对象的时候,在函数开辟栈帧的时候,空间就已经被开辟出来了。

构造函数的语法特性:

  • 函数名与类名相同;
  • 无返回值(不是void,而是返回值不用写);
  • 对象实例化时编译器自动调用对应的构造函数;
  • 构造函数可以重载;
class Date
{
public:
	//全缺省的构造函数 Date d1
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
};
  • 构造函数属于默认成员函数。如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数;如果显示定义,则编译器不自动生成;
  • 编译器自动生成的构造函数对内置类型不做处理,对自定义类型成员调用它的默认成员函数;
  • 默认构造函数可被认为有(以下三种只能出现一种):无参的构造函数全缺省构造函数编译器默认生成的构造函数。建议使用全缺省构造函数。
  • 声明和定义分开,全缺省构造函数中的缺省参数应该写在函数的声明部分,而定义部分不需要写
  • 构造函数实例化时,可以直接 类名+实例名(对应的实参)
    Date d1;
  • 无参的构造函数,传参时不能带括号;
  • C++规定,对象实例化时,必须调用构造函数。

构造函数语法特性分析

通过以下代码,可以帮助理解构造函数的基础特性: 

#include <iostream>
using namespace std;
class Date
{
private:
	int _year;
	int _month;
	int _day;

public:
	使用Init初始化
	//void Init(int year, int month, int day)
	//{
	//	_year = year;
	//	_month = month;
	//	_day = day;
	//}
	//使用构造函数(无参)初始化
	//Date()
	//	// 无参
	//	// 返回类型不需要写
	//	// 对象实例化时自动调用 (主函数中只需要初始化时,就可以自动调用)
	//	// 可以重载
	//{
	//	_year = 1;
	//	_month = 1;
	//	_day = 1;
	//}

	//Date(int year, int month, int day)
	//	// 构造函数可以重载
	//{
	//	_year = year;
	//	_month = month;
	//	_day = day;
	//}

	//全缺省的构造函数
	Date(int year = 1, int month = 1, int day = 1)
		// 构造函数可以重载
	{
		_year = year;
		_month = month;
		_day = day;
	}



	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
};
int main()
{
	/*Date d1;*/
	//使用Init初始化
	//d1.Init(2024, 1, 29);
	Date d1;
	d1.Print();

	Date d2(2024, 2, 1); 
	d2.Print();//2024 - 2 - 1

    Date d3(2025);
	d3.Print(); //2025 - 1 - 1

	return 0;
}

 一般建议构造函数,写为全缺省的构造函数即可:

class Date
{
private:
	int _year;
	int _month;
	int _day;

public:
	//全缺省的构造函数
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
};

无参的构造函数,与全缺省的构造函数,能不能同时使用吗?不能的原因是因为构造函数不能重载吗?

无参的构造函数全缺省的构造函数不能同时使用的原因:

  • 在调用无参的构造函数时,会发生歧义,编译器不知道到底是要调用全缺省参数的构造函数还是无参的构造函数。 

默认构造函数的定义

默认构造函数是什么?默认构造函数,就是编译器生成的吗?不是。

默认构造函数:

默认构造函数无参的构造函数
全缺省的构造函数
编译器自动生成的构造函数
  • 编译器默认生成的构造函数只是默认构造函数其中之一。

编译器生成的默认构造函数的作用

C++把类型分成为内置类型(基本类型)和自定义类型。

  • 内置类型就是语言提供的数据类型,如:int、char、double、指针等;
  • 自定义类型就是我们使用的class、struct、union等自己定义的类型。

编译器默认生成的构造函数:

C++标准中:

  • 对内置类型:不做处理;
  • 对自定义类型:调用它的默认成员函数。

C++11中:内置类型成员变量在类中声明时可以给默认值。

class Date
{
private:
    //C++11中,内置类型成员变量在类中声明时可以给默认值
    //声明给的缺省值
	int _year = 0;
	int _month = 0;
	int _day;//声明内置类型


public:
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
};
int main()
{

	Date d1;
	d1.Print();//0-0--858993460
	return 0;
}

编译器生成的默认构造函数的意义

编译器默认生成构造函数有意义吗?

编译器默认生成构造函数在一些场景下,是很有意义的。

例如,两个栈实现队列。

分析构造函数的需求

分析一个类型成员和初始化需求:

  • 需要写构造函数时,我们就自己写;
  • 不需要写的时候,编译器自己生成。

绝大多数场景下面都需要自己实现。

析构函数

析构函数定义

析构函数,与构造函数功能相反,析构函数不是完成对象本身的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时自动调用析构函数完成对象中资源的清理工作

注意:对象的开辟与销毁都是栈帧进行工作。栈帧销毁,对象也就销毁了。

  • 构造函数完成初始化
  • 析构函数完成清理资源,类似于Destroy

简单来说,就是类似于初始化Destroy函数。

析构函数的价值,防止C语言中Destroy函数忘记。例如,尤其在栈中。

与Destroy函数,最本质的区别是:可以自动调用。

析构函数语法特性

析构函数特殊的成员函数

析构函数的语法特性:

  1. 析构函数名是在类名前加上字符“~”;
  2. 无参数无返回值类型;(构造函数可以有参数,所以可以重载)
  3. 一个类只能有一个析构函数。若无显示定义,系统会自动生成默认的析构函数;
  4. 析构函数不能重载;
  5. 对象生命周期结束时,C++编译系统系统自动调用析构函数;
  6. 编译器生成的默认析构函数,对自定义类型成员调用它的析构函数(与构造函数类似);
  7. 如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数;
  8. 后定义,先析构。
class Stack
{
private:
	DataType* _array;
	int _capacity;
	int _size;
public:
	~Stack()//对象生命周期结束时,C++编译系统系统自动调用析构函数;
	{
		if (_array)
		{
			free(_array);
			_array = NULL;
			_capacity = 0;
			_size = 0;
		}
	}
};
int main()
{
	Stack s;
	return 0;
}

析构函数语法特性分析

typedef int DataType;
class Stack
{
private:
	DataType* _array;
	int _capacity;
	int _size;

public:
    //构造函数
	Stack(size_t capacity = 3)
	{
		_array = (DataType*)malloc(sizeof(DataType) * capacity);
		if (NULL == _array)
		{
			perror("molloc申请空间失败");
			return;
		}
		_capacity = capacity;
		_size = 0;
	}

	void Push(DataType data)
	{
		_array[_size] = data;
		_size++;
	}

	// 析构函数:
	// 无参数(不好能重载);无返回值;析构函数是“~构造函数”
	// 相当于Destroy
	~Stack()//对象生命周期结束时,C++编译系统系统自动调用析构函数;
	{
		cout << "~Stack" << endl;//~Stack
		if (_array)
		{
			free(_array);
			_array = NULL;
			_capacity = 0;
			_size = 0;
		}
	}
};
int main()
{
	Stack s;//~Stack
	//对象生命周期结束时,C++编译系统系统自动调用析构函数;
	return 0;
}

先定义,后析构:

  • 需要满足栈的规则

 

编译器生成的默认析构函数的作用

编译器默认生成的析构函数跟构造函数类似:

  • 对于内置类型不做处理;
  • 自定义类型的成员去调用它的析构。

析构时的顺序

  • 先销毁局部(函数),后销毁全局(static);
  • 先销毁后定义的。先定义,后销毁 。
//析构的顺序
class Date
{
private:

	int _year;
	int _month;
	int _day;

public:
	Date(int year = 0, int month = 0, int day = 0)
	{
		_year = year;
		_month = month;
		_day = day;

	}

	~Date()
	{
		cout << "~Date()" << _year << " " << _month << " " << _day << endl;

	}
};

void func()
{
	Date d4(4);
	static Date d5(5);
}
static Date d7(7);
Date d6(6);
Date d8(8);
static Date d9(9);
Date d10(10);
int main()
{
	Date d1(1);
	Date d2(2);
	func();
	static Date d3(3);
	//先局部 后全局
	// 局部:先定义,后析构,后静态
	// 全局:不管静态是不是,先定义,后析构
	return 0;
}

拷贝构造函数

拷贝构造函数定义

拷贝构造函数,只有单个形参,是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用

可以理解为:在创建对象时,可以创造一个与已存在的对象一模一样的新的对象。

拷贝构造函数语法特性

拷贝构造函数语法特性:

  1. 拷贝构造函数是构造函数的一个重载形式;
  2. 拷贝构造函数的参数只有一个,并且是类类型对象的引用(别名),使用传值方式编译器会直接报错,会引发无穷递归调用;
  3. 若未显式定义,编译器会生成默认的拷贝构造函数。默认的拷贝构造函数对象按内存存储按字节序完成拷贝,,这种拷贝叫做浅拷贝,或者值拷贝;
  4. 编译器生成的默认拷贝构造:对于内置类型:按照字节方式直接拷贝;对于自定义类型,调用其拷贝构造函数。
  5. 编译器默生成的默认拷贝构造函数中,已经可以完成字节序的值拷贝了,还需要自己显式实现;
  6. 类中如果没有涉及资源申请时,拷贝构造函数是否写都可以;一旦涉及到资源申请时,则拷贝构造函数是一定要写的,否则就是浅拷贝。
  7. 拷贝构造函数典型调用场景:使用已存在对象创建新对象;函数参数类型为类类型对象;函数返回值类型为类类型对象。
class Date
{
	//拷贝构造函数  Date d2(d1);
	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
};

拷贝构造函数语法特性分析

class Date
{
private:
	int _year;
	int _month;
	int _day;
public:
	//全缺省的构造函数(默认构造函数)
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	//拷贝构造函数
	Date(const Date& d)
		//为什么用const修饰:防止被拷贝的那一份,被不小心修改
		//为什么用引用(别名):“这里是形参”,不加会一直递归
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
};

int main()
{
	Date d1;
	Date d2(d1);
	return 0;
}

为什么拷贝构造需要用引用:

  • 传值传参要先调用拷贝构造函数。 
  • 调用拷贝构造函数,要先传参,这里传值传参,会形成一个新的拷贝构造。

编译器生成的默认拷贝构造:

  • 对于内置类型:按照字节方式直接拷贝;
  • 对于自定义类型,调用其拷贝构造函数。 
//编译器生成的默认拷贝构造函数
class Time
{
private:
	int _hour;
	int _minute;
	int _second;
public:
	Time()
	{
		_hour = 1;
		_minute = 1;
		_second = 1;
	}
	Time(const Time& t)
	{
		cout << "Time(const Time& t)" << endl;
		_hour = t._hour;
		_minute = t._minute;
		_second = t._second;
	}

};
class Date
{
private:
	//内置类型
	int _year;
	int _month;
	int _day;
	//自定义类型
	Time _t;
	//编译器生成的默认拷贝构造:
	// 对于内置类型:按照字节方式直接拷贝;
	// 对于自定义类型,调用其拷贝构造函数。
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
};

int main()
{
	Date d1;
	Date d2(d1);//Time(const Time& t)
	//用已经存在的d1拷贝d2,此处会调用date类的拷贝构造函数;
	//date类并没有显式定义拷贝构造函数,编译器会给date类生成一个默认的拷贝构造函数
	return 0;
}

到底要不要显式定义拷贝构造函数: 

  • 类中如果没有涉及资源申请时,拷贝构造函数是否写都可以;
  • 一旦涉及到资源申请时,则拷贝构造函数是一定要写的,否则就是浅拷贝。
  • 动态开辟资源的浅拷贝都不行。

深拷贝:一个深拷贝的拷贝构造,各自指向一段内存空间,但它们指向的空间具有相同的内容。

如下为深拷贝的例子。

Stack(const Stack& s)
{
    //s1空间有多大就申请多大的空间
    _array = (DataType*)malloc(s._capacity * sizeof(DataType));
    if (_array == NULL)
    {
        exit(-1);
    }
    for (int i = 0; i < s._size; i++)
    {
        _array[i] = s._array[i];
        _size++;
    }
    _capacity = s._capacity;
}

拷贝构造函数与构造函数:

  • 拷贝构造函数也是构造函数。 

  • 权限可以缩小,但是不能放大。 

赋值运算符重载

运算符重载定义

C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有返回类型,函数名字以及参数列表,其返回值类型与参数列表与普通函数类似。

运算符重载语法特性

运算符重载语法特性:

  1. 函数的名字:关键字operator后面接需要重载的运算符符号,例如:不能operator@;
  2. 函数原型:返回值类型operator操作符(参数列表)。
  3. 不能通过连接其他符号来创建新的操作符;
  4. 重载操作符必须有一个类类型参数;
  5. 用于内置类型的运算符,其含义不能被改变,例如:内置的整形+,不能改变其含义;
  6. 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this;
  7. 笔试经常出现:5个不能重载的运算符:“.*”、“::”、“sizeof”、“?:”、“.”;
class Date
{
    friend bool operator==(const Date& d1,const Date& d2);
public:
	//局部运算符重载 (d1 == d2)
	//bool operator==(Date* this,const Date& d2)
	bool operator==(const Date& d)
	{
		return _year == d._year
			&& _month == d._month
			&& _day == d._day;
	}
};
全局运算符重载:需要友元声明
//bool operator==(const Date& d1,const Date& d2)
//{
//	return d1._year == d2._year
//		&& d1._month == d2._month
//		&& d1._day == d2._day;
//}

运算符语法特性分析 

//运算符重载operator
class Date
{

public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	//局部运算符重载
	//bool operator==(Date* this,const Date& d2)
	bool operator==(const Date& d)
	{
		return _year == d._year
			&& _month == d._month
			&& _day == d._day;
	}
//private:
private:
	int _year;
	int _month;
	int _day;

};
全局运算符重载:
因为没有在类里面,所以要指明对象
全局运算符重载需要成员变量是共有的
//bool operator==(const Date& d1,const Date& d2)
//{
//	return d1._year == d2._year
//		&& d1._month == d2._month
//		&& d1._day == d2._day;
//}

int main()
{
	Date d1(2003, 07, 07);
	Date d2(2009, 02, 06);
	cout << (d1 == d2) << endl;//0
	Date d3(2003, 07, 07);
	cout << (d1 == d3) << endl;//1
	return 0;
}

赋值运算符重载

赋值运算符重载语法特性:

  1. 参数类型:const T&,传递引用可以提高传参效率;
  2. 返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值;
  3. 检测是否自己给自己赋值;
  4. 返回*this:要符合连续赋值的含义;
  5. 赋值运算符,只能重载成类的成员函数,不能重载成全局函数;
  6. 赋值运算符重载只能是类的成员函数;
  7. 用户没有显式实现赋值运算符时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝;内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值;
  8. 如果类中未涉及到资源管理,赋值运算符是否实现都可以;一旦涉及到资源管理则必须要实现。
class Date
{
public:
    //赋值运算符重载函数 d1 = d3;
	Date& operator=(const Date& d)
	{
		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		return *this;
	}
};

//运算符重载operator
class Date
{
public:
	//构造函数
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	//拷贝构造函数
	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

    //赋值运算符重载函数
	// 返回值类型:类&
	// 参数返回:const 类&
	// 函数返回类
	Date& operator=(const Date& d)
	{
		if (this != &d)
            //这里&d是取地址,不是引用,是防止自己给自己赋值
            //this也是地址
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		return *this;
	}
	//private:
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1(2003, 07, 07);
	Date d2(2009, 02, 06);
	d1 = d2; 
	Date d3(2003, 07, 07);
	d1 = d3;
	return 0;
}

赋值运算符,只能重载成类的成员函数,不能重载成全局函数。 

原因:

  • 赋值运算符在类中不显示实现,编译器就会生成一个默认的赋值运算符重载函数;
  • 此时用户再在类外自己实现一个全局的赋值运算符重载,
  • 就会和编译器在类中生成的默认赋值运算符重载发生冲突;
  • 故赋值运算符只能是类的成员函数。

用户没有显式实现赋值运算符时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。

  • 内置类型成员变量是直接赋值的;
  • 而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。

编译器生成的默认赋值运算符重载函数已经可以完成字节序的值拷贝了,但是仍需要自己实现:

  • 如果类中未涉及到资源管理,赋值运算符是否实现都可以;一旦涉及到资源管理则必须要实现。

前置++和后置++重载

前置++和后置++重载语法特性:

  1. 前置++:先加加后使用;this指向的对象函数结束后不会销毁,故以引用方式返回提高效率;
  2. 后置++:重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译期自动传递。
  3. //前置++和后置++重载
    class Date
    {
    public:
    	//++前置
    	Date& operator++()
    	{
    		_day += 1;
    		return *this;
    	}
    	//后置++
    	Date& operator++(int)
    	{
    		Date temp(*this);
    		_day += 1;
    		return temp;
    	}
    };

//前置++和后置++重载
class Date
{
private:
	int _year;
	int _month;
	int _day;

public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	//++前置
	// 前置++:返回+1之后的结果
	 // 注意:this指向的对象函数结束后不会销毁,故以引用方式返回提高效率
	Date& operator++()
	{
		_day += 1;
		return *this;
	}

	//后置++
	//为了与前置++作为区分,参数多了int
	// 后置++:
	// 前置++和后置++都是一元运算符,为了让前置++与后置++形成能正确重载
	// C++规定:后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器自动传递
	// 注意:后置++是先使用后+1,因此需要返回+1之前的旧值,故需在实现时需要先将this保存一份,然后给this + 1
	//而temp是临时对象,因此只能以值的方式返回,不能返回引用
	Date& operator++(int)
	{
		Date temp(*this);
		_day += 1;
		return temp;
	}
};

int main()
{
	Date d2,d3;
	Date d1(2022, 1, 13);
	d2 = d1++;
	d3 = ++d1;
	return 0;
}

const成员函数

const成员函数定义

const成员函数,用const修饰的“成员函数”,const修饰类成员函数。

  • 实际修饰该成员函数隐含的this指针(this指针指向的内容),表明在该成员函数中不能对类的任何成员进行修改

const成员函数语法特性

const成员函数语法特性:

  • 在类中,返回值 成员函数名() const { },例如:
class Date
{
public:
    void Print() const{}
}

int main()
{
    const Date d;
    d.Print();
}

权限可以缩小,但是不能被放大:

只有指针和引用才存在权限放大

  • const对象不可以调用非const成员函数;(权限放大)
  • 非const对象可以调用const成员函数;(权限缩小)
  • const成员函数不可以调用其它的非const成员函数;(权限放大)
  • 非const成员函数可以调用其它的const成员函数。(权限缩小)。

const成员函数,const对象可以访问,非const对象也可以访问;

加不加const,取决于是读,还是读写,看是否修改。

  • 成员函数,如果是一个 对成员函数只进行读访问的函数,建议加const,这样const对象和非const对象都可以使用;
  • 成员函数,如果是一个 对成员函数只进行读写访问的函数,不能加const,否则不能修改成员变量。

 const成员函数语法特性分析

临时变量具有常性

临时变量出现的情况:

  • 类型转化 与 返回值

取地址及const取地址操作符重载

取地址操作符重载const取地址操作符重载,这两个默认成员函数一般用不去重新定义,编译器会默认生成。

取地址及const取地址操作符重载语法特性

取地址及const取地址操作符重载语法特性:

  • 一般用不去重新定义,编译器会默认生成;
  • 只有特殊情况,才需要重载,比如想让别人获取到指定的内容。
  • 代码示例(理解):
class Date
{
public:
    const Date* operator&() const
    {
        return this;
    }
}

【巩固训练】 日期类

学习完了C++中的成员函数,我们就来完成一个Date类吧~

 "Date.h"头文件

// "Date.h"文件中
#pragma once
#include <iostream>
#include <assert.h>
using namespace std;
class Date
{

public:

	int GetMonthDay(int year, int month)
	{
		assert(month > 0 && month < 13);
		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(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);
	Date& operator=(const Date& d)
	{
		this->_year = d._year;
		this->_month = d._month;
		this->_day = d._day;
		return *this;
	}
	// 析构函数
	~Date();
	// 日期+=天数
	Date& operator+=(int day);
	// 日期+天数
	Date operator+(int day);
	// 日期-天数
	Date operator-(int day);
	// 日期-=天数
	Date& operator-=(int day);
	// 前置++
	Date& operator++();
	// 后置++
	Date operator++(int);
	// 后置--
	Date operator--(int);
	// 前置--
	Date& operator--();
	// >运算符重载
	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()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}
	bool CheckInvalid();

	//友元声明
	friend ostream& operator<<(ostream& out, const Date& d);
	friend istream& operator>>(istream& in, Date& d);

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

ostream& operator<<(ostream& out, const Date& d);
istream& operator>>(istream& in, Date& d);

 "Date.cpp"函数定义

// "Date.cpp"文件中
#include"Date.h"
// 全缺省的构造函数
Date::Date(int year, int month, int day)
{
	_year = year;
	_month = month;
	_day = day;

	if (!CheckInvalid())
	{
		cout << "构造日期非法" << endl;

	}
}

// 拷贝构造函数
// d2(d1)
Date::Date(const Date& d)
{
	_year = d._year;
	_month = d._month;
	_day = d._day;
}

// 析构函数
Date::~Date()
{
	_year = 0;
	_month = 0;
	_day = 0;

}

// 日期-=天数
Date& Date::operator-=(int day)
{
	_day -= day;
	while (_day <= 0)
	{
		_month--;
		if (_month == 0)
		{
			_year--;
			_month = 12;
		}
		_day = _day + GetMonthDay(_year, _month);
	}
	return *this;
}

// 日期-天数
Date Date::operator-(int day)
{
	Date tmp=*this;
	tmp -= day;
	return tmp;
}


// 日期+=天数 d1=d1+20
Date& Date::operator+=(int day)
{
	_day += day;
	while (_day > GetMonthDay(_year, _month))
	{
		_day = _day - GetMonthDay(_year, _month);
		_month++;
		if (_month > 12)
		{
			_year++;
			_month = 1;
		}
	}
	return *this;
}
// 日期+天数 d1+10 d1=d1+10   di+=10
Date Date::operator+(int day)
{
	Date tmp(*this);
	tmp += day;
	return tmp;
}

//【方法一】:
// 日期+天数 d1+10
//Date Date::operator+(int day)
//{
//	Date temp(*this);
//	temp._day += day;
//	while (temp._day > GetMonthDay(temp._year, temp._month))
//	{
//		temp._day = temp._day - GetMonthDay(temp._year, temp._month);
//		temp._month++;
//		if (temp._month > 12)
//		{
//			temp._year++;
//			temp._month = 1;
//		}
//	}
//	return temp;
//}
//
// 日期+=天数 d1=d1+20
//Date& Date::operator+=(int day)
//{
//	*this=*this+day;
//	return *this;
//}
// ==运算符重载
bool Date::operator==(const Date& d)
{
	return _day == d._day
		&& _month == d._month
		&& _year == d._year;
}

//d1<d2
bool Date::operator<(const Date& d)
{
	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 false;
}

// <=运算符重载
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);
}

// 前置++
Date& Date::operator++()
{
	*this = *this + 1;
	return *this;
}
// 后置++
Date Date::operator++(int)
{
	Date tmp = *this;
	*this += 1;
	return tmp;
}
// 前置--
Date& Date::operator--()
{
	*this = *this - 1;
	return *this;
}
// 后置--
Date Date::operator--(int)
{
	Date tmp = *this;
	*this -= 1;
	return tmp;
}

// 日期-日期 返回天数
//this-d
int Date::operator-(const Date& d)
{
	int flag = 1;
	Date Max = *this;
	Date Min = d;

	if (*this < d)
	{
		flag = -1;
		Max = d;
		Min = *this;
	}

	int n = 0;
	while (Min != Max)
	{
		Min++;
		n++;
	}
	return flag * n;;
}
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 << "请依次输入年月日:>";
		in >> d._year >> d._month >> d._day;

		if (!d.CheckInvalid())
		{
			cout << "输入非法,请重新输入" << endl;
		}

		else
		{
			break;
		}
	}
	
	
	return in;
}

bool Date::CheckInvalid()
{
	if (_year <= 0
		|| _month < 1 || _month>12
		|| _day<1 || _day>GetMonthDay(_year, _month))
	{
		return false;
	}
	else
	{
		return true;
	}
}

 "Test.cpp"测试样例

// "Test.c"文件中
#define _CRT_SECURE_NO_WARNINGS 1
#include"Date.h"
int main()
{
	
	Date d2(2024, 2,0);
	int day=d2.GetMonthDay(1964, 5);
	cout << day << endl;
	///*Date d2 = d1 + 30000;
	//Date d3 = d1 - 31;*/
	//d1.Print();
	//printf(" \n");
	//d1++;
	//d1.Print();
	//Date d2=d1--;
	//d2.Print();
	//d1.Print();
	///*d1--;
	//d1.Print();
	//--d1;
	//d1.Print();*/
	Date d1(2025, 2, 29);
	Date d2(2025, 1, 29);
	/*int days =d1-d2;
	cout << days << endl;*/
	cout << d2;
	//d2 << cout;
	Date d3,d4,d5;
	cin >> d3>>d4>>d5;
	cout << d3<<d4<<d5;
	return 0;
}
评论 25
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

安心学编程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值