类与对象(中)

1.构造函数
构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,保证每个数据成员有一个合适的初始值,并且在对象的生命周期内只调用一次
构造函数并不是开空间创建对象,而是初始化对象
特征:
①.函数名与类名相同
②.无返回值
③.对象实例化时编译器自动调用的构造函数
④.构造函数可以重载,可以写多个构造函数,提供多种初始化方式

无参构造函数

Date(){
	_year=1;
	_month=1;
	_day=1;
}

带参构造函数

Date(int year,int month,int day){
	_year=year;
	_month=month;
	_day=day;
}

带参缺省值构造函数

Date(int year=2023,int month=7,int day=30){
	_year=year;
	_month=month;
	_day=day;
}
Date d1;//调用无参构造函数
Date d2(2015,1,1)//调用带参的构造函数

如果通过无参构造函数创建对象时,对象后面不用跟括号,不然就成了函数声明

Date d3();

这段代码的意思为:声明一个d3函数,该函数没有参数,返回值为Date类型
⑤.如果类中没有显式定义构造函数,则c++编译器会自动生成一个无参的默认构造函数,一旦用户显示定义编译器将不再生成
默认构造函数
<1> 无参构造函数
<2>全缺省值构造函数
<3>编译器默认生成的构造函数
构造函数,也是默认成员函数,我们不写,编译器会自动生成,编译器生成的默认构造函数的特点:
<1>我们不写才会生成,我们写了就不会生成
<2>内置类型的成员不会处理(c++把类型分成内置类型(基本类型)和自定义类型,内置类型就是语法已经定义好的类型,如int,char等,自定义类型就是自己用class,struct,union定义的类型。编译器生成的默认构造函数对内置类型不做处理,对于自定义类型将调用它们的构造函数)
<3>自定义类型的成员才会处理,会去调用这个成员的默认构造函数
(该自定义类型成员如果没有默认构造函数则会报错,如果不提供默认构造,则需要初始化列表才能解决)

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

特征:①.析构函数名是在类名前加字符~
②.无参数无返回值
③.一个类有且只有一个析构函数,析构函数不能重载,若显式未定义,系统会自动生成默认的析构函数
(系统默认生成的析构函数与系统默认生成的构造函数类似,对于内置类型不会处理,对于自定义类型会自动调用该自定义类型的析构函数,像int*p,不会去释放p所指向的空间,因为指针类型都是内置类型)
④.对象生命周期结束时,c++编译系统会自动调用析构函数

Date d1;
Date d2;
stack st1;
stack st2;
//先调用st2的析构,再是st1,再是d2,再是d1

3.拷贝构造函数
为什么要有拷贝构造函数?
如果采用浅拷贝,将对象浅拷贝,对象中如果有动态开辟的内存,拷贝的对象析构后,动态开辟的内存被释放,而原对象也要析构,所以会造成double free,两次释放的问题
①.拷贝构造函数是构造函数的一个重载形式
②.拷贝构造函数的参数只有一个且必须使用引用传参,使用传值方式会引发无穷递归调用
(因为拷贝构造函数的参数如果是传值传参,先传参,传参要形成一个拷贝构造,拷贝构造又要继续传参

stack(const stack&s){
_array=(int*)malloc(sizeof(int)*capacity;
memcpy(_array,s._array,sizeof(int)*s._size);
_size=s._size;
_capacity=s._capacity;
}

我们不写,编译器默认生成的拷贝构造函数跟之前拷贝构造函数特性不一样
①.内置类型进行值拷贝
②.自定义类型,调用它的拷贝构造
4.赋值运算符重载
①.参数类型
②.返回值
③.检测是否自己给自己赋值
④.返回*this
⑤.一个类如果没有显式定义赋值运算符重载,编译器也会生成一个,完成对象按字节序的拷贝

运算符重载
函数名字为:关键字operator后面接需要重载的运算符符号
函数原型:返回值类型operator操作符(参数列表)
①.不能通过连接其他符号来创建新的操作符,比如operator@
②.重载操作符必须有一个类类型或者枚举类型的操作数
③.用于内置类型的操作符,其含义不能改变,例如内置的整型+不能改变其含义
④.作为类成员的重载函数时,其形参看起来比操作数数目少1成员函数的
操作符有一个默认形参this,限定为第一个形参
⑤.不能改变操作符的操作数个数,一个操作符是几个操作数,那么重载的时候就有几个操作数
⑥.五个运算符不能重载,分别是 .* :: sizeof ?: 丶不能重载

日期类的实现

class Date
 
{
	friend void operator<<(ostream& out, const Date& d);//友**元函数是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字**
	friend void operator>>(istream& out, Date& d);
 
public:
 
	// 获取某年某月的天数
	int GetMonthDay(int year, int month){
	/*	if (month <= 0 || month > 12){ cout << "月份不对" << endl; return 0; }*/
		static int monthDatearry[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
		if ((month==2)&&((year % 4 == 0 && year % 100 != 0) || (year % 100 == 0 && year % 400 == 0))){
			return 29;
		}
		else{
			return monthDatearry[month];
		}
 
	}
 
	// 全缺省的构造函数
 
	Date(int year = 1900, int month = 1 , int day = 1){
		cout << "构造" << endl;
		 _year = year;
		 _month = month;
		 _day = day;//如果声明给了缺省值,没有次语句将使用缺省值;
	}
	//不传参数就可以调用就叫默认构造;(系统默认生成,全缺省,无参数)
	
	//Date(){
	//	_year = 1900;
	//	_month = 1;
	//	_day = 2;
	//}
 
 
	// 拷贝构造函数(自定义类型);
	// d2(d1)
	Date(const Date& d ){//const引用不能修改防止写反
		cout << "拷贝构造" << endl;
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
 
	// 赋值运算符重载
	// d2 = d3 -> d2.operator=(&d2, d3)
	Date& operator=(const Date& d){
		_year = d._year;
		_month = d._month;
		_day = d._day;
		return *this;
	}
 
	// 析构函数
 
	~Date(){
		_year = 0;
 
		_month = 0;
 
		_day = 0;
	}
 
//运算符重载运算符有几个操作数就有几个参数
// 日期+=天数
	Date& operator+=(int day){
		if (day < 0)return *this -= (-day);
		_day += day;
		while (_day>GetMonthDay(_year, _month)){
			_day -= GetMonthDay(_year, _month);
			_month++;
			while (_month == 13){
				_month = 1;
				_year++;
			}
		}
		return *this;
	}
 
 
 
	// 日期+天数
 
	Date operator+(int day)const{
		
		Date ret(*this);
		ret += day;
		return ret;
	}
// 日期-=天数
	Date& operator-=(int day){
		if (day < 0)return *this += (-day);
		_day -= day;
		while (_day<=0){
			--_month;
			if (_month <= 0){
				_month = 12;
				--_year;
			}
			_day += GetMonthDay(_year, _month);			
		}
		return *this;
	}
// 日期-天数
	Date operator-(int day)const{
		Date ret = *this;
		ret -= day;
		return ret;
	}
	// 前置++
	Date& operator++(){
		*this += 1;
		return *this;
	}
	// 后置++,后置++增加了一个形参,加了一个int参数进行占位,跟前置++构成重载进行区分,本质后置++调用,编译器进行特殊处理)
	Date operator++(int){
		Date ret = *this;//Date ret(*this);
		ret += 1;
		return ret;
	}
	// 后置--
	Date operator--(int){
		Date tmp = *this;
		*this -= 1;
		return tmp;
	}
	// 前置--
	Date& operator--(){
		*this -= 1;
		return *this;
	}
	// >运算符重载
	bool operator>(const Date& d)const{
		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;
		}
		return false;
	}
	// ==运算符重载
	bool operator==(const Date& d)const{	
		return _year == d._year
			&&_month == d._month
			&&_day == d._day;
	}
	// >=运算符重载
	bool operator >= (const Date& d)const{
		return *this > d || *this == d;
	}
	// <运算符重载
	bool operator < (const Date& d)const{
		return !(*this >= d);
	}
	// <=运算符重载
	bool operator <= (const Date& d)const{
		return *this < d || *this == d;
	}
	// !=运算符重载
	bool operator != (const Date& d)const{
		return !(*this == d);
	}
 
	// 日期-日期 返回天数
	int operator-(const Date& d)const{
		Date max = *this;
		Date min = d;
		int flag = 1;
		int count = 0;
		if (*this < d){
			flag = -1;
			max = d;
			min = *this;
		}
		while (max != min){
			++count;
			++min;
		}
		return (flag*count);
	}
private:
 
	int _year;
 
	int _month;
 
	int _day;//可以给缺省值,不是初始化,没有空间;
 
};
//成员函数this指针隐藏,默认第一个;流提取和流插入重载一般不写为成员函数;
void operator<<(ostream& out,const Date& d){
	out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
}
void operator>>(istream& in, Date& d){
	in >> d._year;
	in >> d._month;
	in >> d._day;
}

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

void print()const;
void print();

可以同时存在,构成重载
认为const Datethis,Datethis是两种不同的类型,去找最匹配的

//读
const int&operator[](Date*this)  const { 
return _a[i];
}
//写
int&operator[](
return _a[i];
}

const对象不能调用非const成员函数-权限的放大
非const对象可以调用const成员函数-权限的缩小
const成员函数不能调用非const成员函数-权限的放大
非const成员函数可以调用const成员函数-权限的缩小

取地址及const取地址操作符重载
这两个默认成员函数一般不用重新定义,编译器默认生成
除非不想要取到有效地址

const Date*operator&()const{
return nullptr;
}

声明与定义分离,两边都需要加const

你可能感兴趣的:(c语言,c++)