当前位置: 首页 > article >正文

c++——类和对象(中)

目录

  • 一、 类的6个默认成员函数
  • 二、 构造函数
    • 2.1概念
    • 2.2特性
  • 三、 析构函数
    • 3.1概念
    • 3.2 特性
  • 四、 拷贝构造函数
    • 4.1概念
    • 4.2特征
  • 五、赋值运算符重载
    • 5.1 运算符重载
    • 5.2赋值运算符重载
    • 5.3前置++和后置++重载
  • 六、日期类的实现
  • 七、const成员函数
  • 八、取地址及const取地址操作符重载
  • 九、总结


一、 类的6个默认成员函数

如果一个类中什么成员都没有,简称为空类。
空类中真的什么都没有吗?并不是,任何类在什么都不写时,编译器会自动生成以下6个默认成员函数。
默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数。

class Data {};

在这里插入图片描述

二、 构造函数

2.1概念

如下:

class Date
{
public:
	void Init(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}

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

int main()
{
	Date d1;
	d1.Init(2002, 11, 24);
	d1.Print();
	Date d2;
	d2.Init(2007, 1, 11);
	d2.Print();
	return 0;
}

对于Date类,可以通过 Init 公有方法给对象设置日期,但如果每次创建对象时都调用该方法设置信息,未免有点麻烦,那能否在对象创建时,就将信息设置进去呢?
构造函数是一个特殊的成员函数名字与类名相同,创建类类型对象时由编译器自动调用,以保证每个数据成员都有 一个合适的初始值,并且在对象整个生命周期内只调用一次

2.2特性

构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任务并不是开空间创建对象,而是初始化对象

其特征如下

  1. 函数名与类名相同。
  2. 无返回值。(不需要写void)
  3. 对象实例化时编译器自动调用对应的构造函数。
  4. 构造函数可以重载。

在这里插入图片描述

#include<iostream>
using namespace std;

class Date
{
public:
	//无参构造函数
	Date()
	{
		_year = 1;
		_month = 1;
		_day = 1;
	}

	//带参构造函数
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	//它们每个对象的隐含this指针都指向各自对象的地址

	void Print()
	{
		cout << _year << "年" << _month << "月" << _day << "日" << endl;
	}
		
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1;				//注意无参加了括号就成函数声明了
	Date d2(07, 1, 11);
	d1.Print();
	d2.Print();
}

在这里插入图片描述
注意:调用无参对象时不需要加(),否则会认为是函数的声明。

Date d1;
Date d1(); 		//错误,容易和函数声明混淆
  1. 只有当我们没有显式定义构造函数,编译器才会自动生成一个无参构造函数。
#include<iostream>
using namespace std;

class Date
{
public:
	
	/*
	//无参构造函数
	Date()
	{
		_year = 1;
		_month = 1;
		_day = 1;
	}

	//带参构造函数
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	//它们每个对象的隐含this指针都指向各自对象的地址
	*/

	void Print()
	{
		cout << _year << "年" << _month << "月" << _day << "日" << endl;
	}
		
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1;				//注意无参加了括号就成函数声明了
	//Date d2(07, 1, 11);
	d1.Print();
	//d2.Print();
}

在这里插入图片描述

我们没有传入值到类中。因此当使用编译器的无参构造函数,类的内置类型不会被初始化。

全缺省能代替无参功能
无参的、全缺省的、我们不写编译器生成的都称为默认构造函数

  1. 编译器自动生成的默认构造函数,对内置类型的对象不做处理,只调用自定义类型对象构造函数。
class test
{
public:
	test()
	{
		cout << "huangyanling" << endl;
	}

private:
	int x;
	int y;

};

class Date
{

public:
	void Init(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	void Init()
	{
		_year = 07;
		_month = 1;
		_day = 11;
	}

	void Print()
	{
		cout << _year << "年" << _month << "月" << _day << "日" << endl;
	}

private:
	//内置类型
	int _year;
	int _month;
	int _day;
	//自定义类型
	test a;
};


int main()
{
	Date d1;

	d1.Print();
	return 0;
}

在这里插入图片描述

当创建基于Date类的对象d1时,编译器默认生成无参构造函数,去调用了自定义类型 test类的构造函数。
默认无参构造函数不处理内置类型,所以在打印Date类中的内置类型year,month,day才会出现随机值。

编译器生成的默认构造函数(初始化):
1.内置类型成员,不去调用默认构造(有的编译器会调用默认构造),我们就当做它不调用默认构造,我们不去依赖编译器初始化
2.自定义类型成员,会去调用默认构造

7.无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个被认为是默认构造函数的有三种:

  • 显示的无参构造函数
Date()
{
	_year = 2007;
	_month = 1;
	_day = 11;
}
  • 显示的全缺省构造函数
Date(int year = 2007, int month = 1, int day = 11)
{
	_year = year;
	_month = month;
	_day = day;
}
  • 编译器默认生成的构造函数

注意:默认构造函数是指不传参就可以调用的构造函数
上面提到的无参构造函数和全缺省函数。

结论:
不能依靠编译器的情况:
一般情况下,构造函数都需要我们自己写
可以依靠编译器的情况(一般较少)
1.内置类型成员都有缺省值,且初始化符合我们的要求
2.全是自定义类型的构造,且这些类型都定义默认构造

三、 析构函数

3.1概念

通过前面构造函数的学习,我们知道一个对象是怎么来的,那一个对象又是怎么没呢的?
析构函数:与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由
编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作

3.2 特性

析构函数是特殊的成员函数,其特征如下:

  1. 析构函数名是在类名前加上字符 ~
  2. 无参数无返回值类型。(不需要写void)
  3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构
    函数不能重载
  4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。
#include<iostream>
using namespace std;

class Date
{
public:
	//构造
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	//析构
	~Date()
	{
		cout << "~Date" << endl;
		if (_year || _month || _day)
		{
			_year = 0;
			_month = 0;
			_day = 0;
		}
	}
	
	void Print()
	{
		cout << _year << "年" << _month << "月" << _day << "日" << endl;
	}
	
private:
	int _year = 07;
	int _month = 1;
	int _day = 11;

};


void test()
{
	Date d;
	d.Print();
}

int main()
{
	test();
	return 0;
}

test函数内部创建了Date类的对象d,test函数调用结束,对象d离开了作用域,对象在销毁时会自动调用析构函数,完成对象中资源的清理工作
可以看到这里调用了析构函数
在这里插入图片描述

  1. 需不需要写析构的情况
  • 在动态栈中需要malloc动态申请空间,对象在销毁时会自动调用它的析构函数
class Stack
{
	Stack(int capacity = 4)
	{
		_a = (int*)malloc(sizeof(int) * capacity);
		if (nullptr == _a)
		{
			perror("malloc fail");
			return;
		}
		_capacity = capacity;
		_top = 0;
	}
	
	~Stack()
	{
		cout << "~Stack()" << endl;
		free(_a);
		_a = nullptr;
		_capacity = _top = 0;
	}
	
private:
	int *_a;
	int _capacity;
	int _top;
};
  • 没有动态申请,不需要写析构
class Date
{
private:
	int _year;
	int _month;
	int _day;	
};
  • 需要释放资源的成员都是自定义类型,不需要写析构。
class MyQueue
{
private:
	Stack _pushst;
	Stack _popst;
};

同样析构对内置类型成员不做处理,对自定义类型会去调用它的析构函数

四、 拷贝构造函数

4.1概念

拷贝构造函数会创建一个新对象,并用另一个同类型的对象初始化,生成一个新的对象副本。
拷贝构造只有一个参数,规定使用引用传参(一般用const修饰),使用已存在的类类型对象创建时编译器自动调用。

4.2特征

拷贝构造函数也是特殊的成员函数,其特征如下:

  1. 拷贝构造函数是构造函数的一个重载形式
  2. 拷贝构造函数的参数只有一个必须是类的类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用。
#include<iostream>
using namespace std;

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


	//隐含的this指针对象是d2,这里形参d是d1
	//我们一般在这里加个const权限平移或缩小,可以及时发现这里赋值的顺序是否写反
	
	/*以下这两种写法都可以
	Date(Date& d)
	{
		_year = d._year;	
		_month = d._month;
		_day = d._day;
	}
	*/
	
	Date(const Date& d)
	{
		this->_year = d._year;
		this->_month = d._month;
		this->_day = d._day;
	}
	
	void Print()
	{
		cout << _year << "年" << _month << "月" << _day << "日" << endl;
	}

private:
	int _year;
	int _month;
	int _day;

};

//以下形参错误,造成无穷递归
//自定义类型拷贝须引用或指针,但我们通常用引用
void func(Date d)
{


}

//内置类型可以直接写
void func(int n)
{

}
int main()
{
	Date d1(2007, 1, 11);
	Date d2(d1);			//d2拷贝成d1
	d1.Print();
	d2.Print();


	func(d1);		//自定义类型
	func(17);		//内置类型

	return 0;
}

在这里插入图片描述

内置类型成员完成值拷贝/浅拷贝
自定义类型成员会调用它的拷贝构造,调用之后又要继续往下调用它的拷贝构造,造成无穷递归在这里插入图片描述

  1. 若未显式定义,编译器会生成默认的拷贝构造函数。默认的拷贝构造函数对象按内存存储按
    字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。
  • 浅拷贝
#include<iostream>
using namespace std;

class Date
{
public:
	Date(int year = 1, int month = 1, int day = 11)
	{
		_year = year;
		_month = month;
		_day = day;
	}
   
	void Print()  
	{
		cout << _year << "年" << _month << "月" << _day << "日" << endl;
	}

private:
	int _year;
	int _month;
	int _day;

};



int main()
{
	Date d1(2007, 1, 11);
	Date d2(d1);
	d1.Print();
	d2.Print();
	return 0;
}

如上对于内置类型我们可以不写拷贝构造,编译器会自动生成,这种内置类型的拷贝叫浅拷贝或值拷贝
在这里插入图片描述

  1. 涉及资源申请的类需要使用深拷贝
  • 深拷贝
    引入:

其中一种场景
在这里插入图片描述

如栈这种动态需要申请的就需要我们显式的去完成深拷贝。

#include<iostream>
#include<stdlib.h>
using namespace std;
class Stack
{
public:
	Stack(int capacity = 4)
	{
		cout << "Stack()" << endl;
		_a = (int*)malloc(sizeof(int) * capacity);
		if (nullptr == _a)
		{
			perror("malloc fail");
			return;
		}
		_capacity = capacity;
		_top = 0;
	}

	//st2(st1)
	Stack(const Stack& st)
	{
		//st2创建一个和st1一样大的空间
		_a = (int*)malloc(sizeof(int) * st._capacity);
		if (nullptr == _a)
		{
			perror("malloc fail");
			return;
		}
		//st1的数据拷贝给st2
		memcpy(_a, st._a, sizeof(int) * st._top);
		_capacity = st._capacity;
		_top = st._top;
	}

	~Stack()
	{
		cout << "~Stack()" << endl;
		free(_a);
		_a = nullptr;
		_capacity = _top = 0;
	}

private:
	int* _a;
	int _capacity;
	int _top;
};
int main()
{
	Stack st1;
	Stack st2(st1);
}

正常运行调用了两次析构
在这里插入图片描述

  1. 编译器生成的默认拷贝构造函数可以完成字节序的值拷贝,一旦涉及到资源申请时,则拷贝构造函数是一定要写的,否则就是浅拷贝。
  2. 拷贝构造函数典型调用场景。
    使用已存在对象创建新对象
    函数参数类型为类类型对象
    函数返回值类型为类类型对象

五、赋值运算符重载

对于内置类型可以直接比较大小
那对于自定义类型是不可以的

int main()
{
	int a = 7;
	int b = 2;
	cout << (a < b)<< endl;		
	//流插入的优先级是很高的要加括号
	
	Date d1(2002, 11, 24);
	Date d2(2007,1,11);
	cout << (d1 < d2) << endl;
	return 0;
}

如下我们引出自定义也可以比较的操作符

5.1 运算符重载

为了使代码更易读和更易于维护,C++引入了运算符重载。运算符重载是拥有特殊函数名的函数,函数名为关键字operator,该函数和普通函数一样有返回类型。

函数名字为:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)

注意:
1.不能通过连接其他符号来创建新的操作符:比如operator@
2.重载操作符必须有一个类类型参数(因为重载运算符的目的是用于自定义类型的使用)
3.用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义
4.作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this
5.(.*) ( :: ) (sizeof) ( ?: ) (.) 注意这5个运算符不能重载。这个经常在笔试选择题中出现。

//重载==运算符,对两个自定义类型进行比较,只需要传入一个类对象的参数即可
bool operator==(const Date & d)//尽量使用引用传入
{
	return _year == d._year
		&& _month == d._month
		&& _day == d._day;
}
 

// bool operator==(Date* this, const Date& d2)
// 这里需要注意的是,左操作数是this,指向调用函数的对象
bool operator==(const Date& d2)
{
	return _year == d2._year;
	 && _month == d2._month
	 && _day == d2._day;
}

5.2赋值运算符重载

  1. 重载格式
  • 参数类型:const 类名&,传递引用可以提高传参效率

  • 返回值类型:类名&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值(注意:返回的是局部变量就不能用引用)

  • 检测是否自己给自己赋值

     d1=d1; 
    
  • 返回*this :要复合连续赋值的含义

#include<iostream>
#include<stdbool.h>
using namespace std;

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

	void Print()
	{
		cout << _year << "年" << _month << "月" << _day << "日" << endl;
	}
   
	//注意返回类型,出了作用域对象还在就可以用引用返回
	Date& operator=(const Date& d)
	{
		//为了防止自己赋值给自己(d1=d1)
		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		//this是指向当前类对象的指针
		return *this;	
	}

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

	int _year;
	int _month;
	int _day;
};

//直接比较它们的对象传引用,函数内部不改变加const
//比较两个日期中小的那个,返回x1是否比x2小
bool operator<(const Date& x1, const Date& x2)
{
	if (x1._year < x2._year)
	{
		return true;
	}

	else if (x1._year == x2._year && x1._month < x2._month)
	{
		return true;
	}

	else if (x1._year == x2._year && x1._month == x2._month && x1._day < x2._day)
	{
		return true;
	}

	return false;
}


int main()
{
	Date d1(2002, 11, 24);
	Date d2(2007,1,11);
	d1 < d2;

	
	d1 = d2;			//已经存在的两个对象之间的拷贝——运算符重载函数

	//Date d3(d1);		//用一个存在的对象初始化另一个对象——构造函数



	//注意赋值函数的返回值
	Date d4, d5;
	d5 = d4 = d1;
	return 0;
}     

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

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	int _year;
	int _month;
	int _day;
};
// 赋值运算符重载成全局函数,注意重载成全局函数时没有this指针了,需要给两个参数
Date& operator=(Date& d1, const Date& d2)
{
	if (&d1 != &d2)
	{
		d1._year = d2._year;
		d1._month = d2._month;
		d1._day = d2._day;
	}
	return d1;
}
// 编译失败:
// error C2801: “operator =”必须是非静态成员

原因:赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的成员函数。
3.用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。注意:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1(2002, 11, 24);
	Date d2;
	d2 = d1;		//可以不写赋值运算符重载
}

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

默认生成赋值重载跟拷贝构造行为一样
1、内置类型成员–值拷贝/浅拷贝
2、自定义类型成员会去调用他的赋值重载

5.3前置++和后置++重载

  • 前置++重载
//this指向的对象函数结束后不会销毁,故以引用方式返回提高效率
Date& operator++()
{
	_day++;
	return *this;
}
  • 后置++重载
Date operator++(int)
{
	Date temp(*this);		//拷贝构造
	_day++;
	return temp;	
}

为了让前置++与后置++形成能正确重载, C++规定:后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器自动传递注意:后置++是先使用后+1,因此需要返回+1之前的旧值,故需在实现时需要先将this保存一份,然后给this+1, 而temp是临时对象,因此只能以值的方式返回,不能返回引用

六、日期类的实现

在实现日期类我们可以先写一个小于或大于函数和一个等于函数,可以直接复用更方便

Date.h文件
#pragma once
#include <iostream>
#include <assert.h>
using namespace std;

class Date
{
	//2.友元函数声明
	friend ostream& operator<<(ostream& out, const Date& d);
	friend istream& operator>>(istream& in, Date& d);

public:
	Date(int year = 1, int month = 1, int day = 1);
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
	bool operator<(const Date& x);
	bool operator==(const Date& x);
	bool operator<=(const Date& x);
	bool operator>(const Date& x);
	bool operator>=(const Date& x);
	bool operator!=(const Date& x);



	int GetMonthDay(int year, int month);	//计算闰年的子函数
	Date& operator+=(int day);				//日期+天数。+=有返回值因为有连续赋值的情况
	Date operator+(int day);				//自己值不发生改变,不用引用,复用+=

	Date& operator-=(int day);				//日期-天数
	Date operator-(int day);

	Date& operator++();						//前置++,返回的值发生了改变	
	Date operator++(int);					//后置++,返回的值没发生改变

	Date& operator--();						//前置--
	Date operator--(int);

	int	operator-(const Date& day);			//日期-日期

	//Date对象默认占用第一个参数,做了左操作数,一般写在全局中
	void operator<<(ostream& out);

	//1.给全局提供一个共有成员函数
	/*
	int GetYear()
	{
		return _year;
	}
	*/

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

//写在全局out就能做第一个参数了
//定义在全局访问不了私有,1.在日期类加一个公有成员函数或2.友元函数
ostream& operator<<(ostream& out, const Date& d);
istream& operator>>(istream& in, Date& d);
Date.cpp文件
#include "Date.h"
Date::Date(int year, int month, int day)
{
	if(month>0&&month<13&&day>0&&day<=GetMonthDay(year,month))
	{
		_year = year;
		_month = month;
		_day = day;
	}
	else
	{
		cout << "非法日期" << endl;
		assert(false);
	}

}

bool Date::operator<(const Date& x)
{
	if (_year < x._year)
	{
		return true;
	}
	if (_year == x._year && _month < x._month)
	{
		return true;
	}
	if (_year == x._year && _month == x._month && _day < x._day)
	{
		return true;
	}
	return false;
}
bool Date::operator==(const Date& x)
{
	return _year == x._year
		&& _month == x._month
		&& _day == x._day;
}
//有了小于函数和等于函数后,这里我们可以直接复用来实现功能
bool Date::operator<=(const Date& x)
{
	return *this < x || *this == x;
}
bool Date::operator>(const Date& x)
{
	return !(*this <= x);
}
bool Date::operator>=(const Date& x)
{
	return !(*this < x);
}
bool Date::operator!=(const Date& x)
{
	return !(*this == x);
}

//对于内置类型一般不考虑引用
int Date::GetMonthDay(int year, int month)
{
	static int daysArr[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
	//只有2月的时候判断
	if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
	{
		return daysArr[2] = 29;
	}
	else
	{
		return daysArr[month];
	}
}

//日期+天数
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)
{
	Date tmp(*this);

	//复用+=函数,这里+的函数没有改变,改变的是拷贝构造tmp的临时变量
	tmp += day;
	return tmp;
}


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

//后置++
//这里加个int参数不是为了接收具体某个值,仅仅是为了区分前后置++,构成重载
Date Date::operator++(int)
{
	Date tmp(*this);
	*this += 1;
	return tmp;
}


Date& Date::operator-=(int day)
{
	//如果传进来的是负数
	if (day < 0)
	{
		return *this += -day;
	}
	_day -= day;
	while (_day < 1)
	{
		//当前月的天数已经减完了
		--_month;
		if (_month == 0)
		{
			_month = 12;
			--_year;
		}
		//获取上个月的天数
		_day += GetMonthDay(_year, _month);
	}
	return *this;
}

//复用-=
Date Date::operator-(int day)
{
	Date tmp = *this;
	tmp -= day;
	return tmp;
}

//前置--
Date& Date::operator--()
{
	*this -= 1;
	return *this;

}
Date Date::operator--(int)
{
	Date tmp = *this;
	*this = *this - 1;
	return tmp;
}

//日期-日期
int	Date::operator-(const Date& day)
{
	Date max = *this;
	Date min = day;
	int flag = 1;

	if (*this < day)
	{
		max = day;
		min = *this;
		flag = -1;
	}

	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)
{
	int year, month, day;
	in >> year >> month >> day;
	if (month > 0 && month < 13 && day>0 && day <= d.GetMonthDay(year, month))
	{
		d._year = year;
		d._month = month;
		d._day = day;
	}
	else
	{
		cout << "非法日期" << endl;
		assert(false);
	}
	return in;
}

test.cpp
#include "Date.h"
//实现一个日期加多少天数后返回日期
void TestDate1()
{
	Date d1(2024, 1, 11);
	d1 += -66;
	d1.Print();

	Date d2(2024, 1, 11);

	//两种写法等价
	//Date d3(d2 + 17);
	Date d3 = d2 + 17;

	d2.Print();
	d3.Print();
}

//前后置++
void TestDate2()
{
	Date d1(2007, 1, 11);
	Date ret1 = d1++;					 //d1.operator++()
	d1.Print();
	ret1.Print();


	Date d2(2007, 1, 11);
	Date ret2 = ++d2;					//d2.operator++(0)括号内数字随便可以
	d2.Print();
	ret2.Print();
}

//日期-天数
void TestDate3()
{
	Date d1(2024, 12, 8);
	d1 -= 300;
	d1.Print();
	Date d2(2024, 12, 8);
	d2 - 300;
	d2.Print();
}

//日期+负的天数或-负的天数
void TestDate4()
{
	Date d1(2024, 12, 8);
	Date d2(2024, 12, 8);
	d1 += -300;
	d2 -= -300;
	d1.Print();
	d2.Print();
}

//日期-日期
void TestDate5()
{
	Date d1(2024, 12, 8);
	Date d2(1923, 2, 15);
	cout << d1 - d2 << endl;
	cout << d2 - d1 << endl;
}

//前后置--
void TestDate6()
{
	Date d1(2024, 12, 8);
	Date ret1 = d1--;
	ret1.Print();			//输出时只有ret1的值没有发生改变
	d1.Print();

	Date d2(2024, 12, 8);
	Date ret2 = --d2;
	ret2.Print();
	d2.Print();
}

//流插入是一个双操作数,流提取
void TestDate7()
{
	Date d1(2024, 12, 11);
	d1 += 100;

	//流插入不能写成成员函数,因为Date对象默认占了左操作数
	//在成员函数
	//d1 << cout;	//d1.operator<<(cout);


	//cout是个ostream的类对象,d1是日期类对象
	//ostream是iostream库定义的
	cout << d1;



	Date d2(2024, 12, 11);
	Date d3(1949, 10, 1);
	cout << d2 << d3 << d1;

	cin >> d1 >> d2;
	cout << d2 << d1;
}


int main()
{
	TestDate1();
	TestDate2(); 
	TestDate3();
	TestDate4();
	TestDate5();
	TestDate6();
	TestDate7();
	return 0;
}

函数重载支持自动识别类型
运算符重载是为了自定义类型支持运算符
它们都有重载但是它们之间没有关系,它们是独立的,
两个运算符重载能构成函数重载

  1. 可以直接支持内置类型是库里面实现了
  2. 可以直接支持自动识别类型是因为函数重载

七、const成员函数

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

(1)权限只能平移或缩小,不能放大
(2)const成员函数内部和非const成员函数内部均可以调用其他常量成员函数或非常量成员函数
(3)成员函数内部不需要修改成员变量时,都可以加上const,方便常量对象调用。
(4)const成员函数中不能对类的任何成员进行修改。
在这里插入图片描述如果不改变成员函数可以都加上const

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

这两个默认成员函数一般不用重新定义 ,编译器默认会生成。

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
class Date
{
public:
	//不想让别人取到这个普通对象地址
	Date* operator&()
	{
		cout << "Date* operator&()" << endl;
		return nullptr;
	}
	const Date* operator&()const
	{
		cout << "const Date* operator&()const" << endl;
		return this;
	}
private:
	int _year=1;
	int _month=1;
	int _day=1;
};
int main()
{
	Date d1;
	const Date d2;
	cout << &d1<<endl;
	cout << &d2<<endl;
	return 0;
}

在这里插入图片描述
这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需要重载,比如想让别人获取到指定的内容。
很少有使用场景

九、总结

在这里插入图片描述


http://www.kler.cn/a/501413.html

相关文章:

  • Bash语言的语法糖
  • 【微服务】面试 1、概述和服务发现
  • 【C++】string的关系运算与比较分析
  • 【前端动效】原生js实现拖拽排课效果
  • 2025年第三届“华数杯”国际赛A题解题思路与代码(Python版)
  • 互斥信号量的等待与通知
  • 《零基础Go语言算法实战》【题目 2-4】一个函数中的多个值
  • Clojure语言的数据结构
  • 【微服务】面试 7、幂等性
  • 【零基础入门Go语言】struct 和 interface:Go语言是如何实现继承的?
  • 高可用虚拟IP-keepalived
  • 51单片机——I2C-EEPROM
  • 视频编辑最新SOTA!港中文Adobe等发布统一视频生成传播框架——GenProp
  • MySQL innodb 引擎架构图
  • 微信小程序订阅消息提醒-云函数
  • 软件测试预备知识⑤—搭建 DNS 服务器
  • OpenCV实现基于拉普拉斯算子的浮雕特效
  • Python Selenium 库学习指南
  • 【Rust】引用与借用
  • uniapp区域滚动——上划进行分页加载数据(详细教程)
  • Python脚本自动创建GitHub标签
  • 制造企业“数字化转型”典型场景参考
  • Excel多层嵌套IF条件写法
  • Android中的蓝牙:BLE、经典蓝牙
  • Golang中遇到“note module requires Go xxx”后的解决方案,不升级Go版本!
  • 数据结构与算法之二叉树: LeetCode 572. 另一棵树的子树 (Ts版)