【C++】类的默认成员函数

1.构造函数
2.拷贝构造函数
3.析构函数、
4.运算符重载---实现一个日期计算器

c++类有6个默认成员函数:构造函数,拷贝构造函数,析构函数,赋值操作符重载。取地址操作符重载,const修饰的取地址操作符重载。其中前四个默认成员函数是我们重点研究对象。

1.构造函数

成员变量为私有的,要对他们进行初始化,必须用一个公有成员函数来进行。同时这个函数有且仅在定义对象时自动执行一次,这时调用的函数称为构造函数。                                                                                                                                                                                     构造函数是特殊的成员函数,其特征如下:

1.1函数名与类名相同。
1.2无返回值
1.3对象构造函数(对象实例化)时系统自动调用对应的构造函数。
1.4构造函数可以重载
1.5构造函数可以在类中定义也可以在类外定义
1.6如果类定义中没有给出构造函数,则c++编译器会默认生成一个缺省的构造函数,但只要我们定义了一个构造函数,系统就不会自动生成缺省的构造函数。
1.7无参的构造函数和全缺省值的构造函数都认为是缺省的构造函数,并且缺省的构造函数只能有一个。

无参的构造函数和带参的构造函数
class Date
{
public:
        Date()  //无参的构造函数
	{}

	Date(int year,int month,int day) //带参的构造函数
	{
		_year = year;
		_month = month;
		_day = day;
	}

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

void test
{
   Date d1;  //调用无参的构造函数
   Date d2(2018,9,24);  //调用带参的构造函数
   Date d3();   //注意这里没有调用对d3的构造函数定义出d3
}

构造函数最好定义成全缺省的,如下:

Date(int year = 1999,int month = 1,int day = 1)//最好定义为全缺省 
	{
		//检查日期是否合法

		if(year < 1900
			|| month < 1 || month > 12
			|| day < 1 || day > GetMonthDay(year,month))
		{
			cout<<"非法日期"<<endl;

		}

		_year = year;
		_month = month;
		_day = day;
	}
GetMonthDay()是一个获取一个月有多少天的函数,实现如下:
int GetMonthDay(int year,int month)
{
	 static int MonthDay[13] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
	 int day = MonthDay[month];
		 if(month == 2
			 && ((year%400 == 0) || (year%4 == 0 && year%100 != 0)))
		 {
			 day += 1;
		 }
		 return day;
}

2.拷贝构造函数
创建对象时使用同类对象来进行初始化,这时所用的函数称为拷贝构造函数,拷贝构造函数是特殊的构造函数。
特征如下:
2.1拷贝构造函数其实是一个构造函数的重载
2.2拷贝构造函数的参数必须使用引用传参,使用传值方式会引发无穷递归。(why?会在下面讲解)
2.3若未显示定义,系统会默认生成一个缺省的拷贝构造函数。缺省的拷贝构造函数会依次拷贝类成员及进行初始化

为什么传值方式会引发无穷递归?

//为什么传值会引发无穷递归
//Date d2(d1)
Date(Date d)
{
 _year = d._year;
}
假如我们要拿d1拷贝构造一个d2,用如上传值的拷贝构造函数来实现的话是有如下实现模型:把d1传给d,d这个对象不存在,实际上就是拿d1拷贝构造一个d;拷贝构造的时候又要传参。。拷贝传参拷贝传参这样一直下去就是一个无穷递归调用。

  我们这里使用日期类的例子来讲解类的默认成员函数,日期类可以不用自己定义拷贝构造函数,但是有的类就必须自己实现一个拷贝构造函数,比如顺序表。顺序表系统自动生成的用不了,因为会产生内存泄漏,这其实是一个浅拷贝的问题,这个问题暂时就不进行详细说明。

        Date(const Date& d)  //拷贝构造函数 
  //参数为了防止在函数内发生意外的改变,最好加上const
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}    为什么这里对象可以直接访问私有的成员变量?1.在类的成员函数中可以直接访问同类对象的私有/保护成员。。。。2.c++的访问限定符是以类为单位的,也就是说在这个单元内的成员可以互相访问。


   Date d1;
   Date d2 = d1;  //调用拷贝构造函数
   Date d3(d1);   //调用拷贝构造函数

 3.析构函数
当一个对象生命周期结束时,c++编译器会自动调用一个成员函数,这个特殊的成员函数叫析构函数。
特征
3.1析构函数在类名前面加~。
3.2析构函数无参数无返回值
3.3一个类有且只有一个析构函数,若未显示定义,系统会自动生成缺省的析构函数。
3.4对象生命周期结束时,c++编译器会自动调用析构函数。
3.5注意析构函数函数体内并不是删除对象,而是做一些清理工作。这里的日期类可以不清理,但有的类必须及逆行清理,比如顺序表vector,不然会内存泄露,清不清理主要看有没有动态的资源

4.运算符重载
运算符为了增强程序的可读性
4.1特征
4.1.1operator+合法的运算符 构成函数名(重载<运算符函数名:operator<)
4.1.2重载运算符以后,不能改变运算符的优先级/结合性/操作数个数。
4.2考点:
5个c++不能重载的运算符:*   ::    sizeof    ?:    .
4.3赋值运算符重载 
 拷贝构造函数是创建的对象,使用一个已有对象来初始化这个准备创建的对象。赋值运算符的重载是对一个已存在的对象进行拷贝赋值

//d2 = d3  =>  d2.operator=(&d2,d3)
Date& operator=(const Date& d)     //有俩个参数,但只写一个,因为还有一个参数是隐含的this指针。
{
	if(this != &d)          //是否是自己给自己赋值。没有什么很坏的影响,只是白做了而已。
	{
	   this->_year = d._year;             //this可以显示的写出来,也可以不写,写着this在这里方便于观察。
	   this->_month = d._month;
	   this->_day = d._day;
	}
	return *this;   //赋值操作已经完成,为什么有个返回值?因为赋值运算符的重载支持三个数,i=j=k;k先赋给j后有一个返回值j,将这个返回值赋给i,返回的是同类型对象,所以类型为Date
		//但此时如果没有给函数类型加引用,就是传值返回,不会直接返回,会创建一个临时对象。。会多一次拷贝构造,拷贝一个临时对象,再拿这个临时对象做返回
		//传值返回:返回的是一个临时对象
		//传引用返回:返回的对象出了作用域还在
}


Date d1;
Date d2(d1);  //调用拷贝构造函数

Date d3;
d3 = d1;   //调用赋值运算符的重载

传值返回和传引用返回:传值返回:返回的是一个临时对象
        传引用返回:返回的对象出了作用域还在

通过运算符的重载我们可以实现一个更好玩的东西:如下图

要完成这个,我们得实现以下的函数

Date& operator+=(int day)
Date& operator+(int day)
Date& operator-=(int day)
Date& operator-(int day)
int operator-(const Date& d) 

实现如下:

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;
	}

	//d+10
	Date operator+(int day)
	{
		Date ret(*this);//*this 是d
		ret += day;

		return ret;
	}
Date& operator-=(int day)
	{
		if(day < 0)
		{
			return *this += -day;
		}
		_day -= day;
		while(_day <= 0)
		{
			--_month;
			if(_month == 0)
			{
				_year--;
				_month = 12;
			}
			_day+=GetMonthDay(_year,_month);
		}
		return *this;
	}
	Date operator-(int day)
	{
		Date ret(*this);

		ret -= day;
		return ret;
	}
int operator-(const Date& d)     //不加const d2会被改
	{
		int flag = 1;
		Date max = *this;
		Date min = d;

		if(*this < d)
		{
			max = d;
			min = *this;
			flag = -1;
		}
		int day = 0;
		while(min < max)
		{
			++(min);
			++day;
		}
		return day*flag;
	}

	//++d  => d.operator++(&d)
	Date& operator++()    //前置   返回值是++后的值
	{
		*this += 1;
		return *this;
	}

	//d++  => operator++(&d,0)
	Date operator++(int)  //后置    int只是为了与前置做一个区分   返回的是++前的值
	{
		Date ret(*this);
		*this += 1;
		return ret;
	}

	Date& operator--()
	{
		*this -= 1;
		return *this;
	}
	Date operator--(int)
	{
		Date ret(*this);
		*this -= 1;
		return ret;
	}


	bool 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 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);
	}
	bool operator!=(const Date& d)
	{
		return !(*this == d);
	}

	void Display()
	{
		cout<<_year<<"-"<<_month<<"-"<<_day<<endl;
	}
	 

日期类的完整实现在我的下一个博客https://blog.csdn.net/hgelin/article/details/82799757中,可以直接拷贝到编译器进行编译。

猜你喜欢

转载自blog.csdn.net/hgelin/article/details/82828771