C++ 类与对象(三)

1.赋值运算符重载

        1.1运算符重载:

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

函数名字为:关键字operator后面接需要重载的运算符符号。

函数原型:返回值类型 operator 操作符(参数列表)

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

这里解释一下    .*

例子:实现 等于运算符重载(全局的)

重载成全局的无法访问私有成员变量

如何实现访问私有成员变量呢?

1.提供这些成员的get和set

2.友元函数

3.重载为成员函数(下面运用了方法3)

1.2赋值运算符重载:

赋值运算符重载格式:
参数类型:const T&,传递引用可以提高传参效率
返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
检测是否自己给自己赋值
返回*this :要复合连续赋值的含义

        拷贝构造与赋值重载区别:

       实现赋值运算符重载

1.3函数返回类型 传值与引用

从赋值运算符成员函数的函数返回值类型提出的问题

引子:左边是传值,右边是引用

理解过程:传值

引用:

针对这一代码,我们选值是正确的,func()函数中 d对象会提前析构,main()函数中 ref对象 接受的是已经被系统收回的空间(野指针),会用风险,比如后面加了 fx()函数  ,ret对象之前接受的值就会发生变化。

赋值运算符重载成员函数返回类型是  Date& ,原因有2个:1.返回的是 *this,而 this所指的对象 存在于main()函数中不会因为 赋值运算重载函数的销毁而析构 this所指的对象,并没有发生上面的情况;2.传值返回和引用返回都可以选,但是引用返回可以减少拷贝构造函数的次数,大大提高了效率,传值返回并不能减少,看下图相同的代码调用函数的次数。

1.4注意:

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

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

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

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


2.日期类的实现

2.1思路:

日期类 编译器默认生成的拷贝构造函数,赋值运算符函数,析构函数就可以;构造函数加个缺省值即可。

可以先实现  小于运算符重载函数 和 等于运算符重载函数 ,从而实现:小于等于,大于,大于等于,不等于   ,5个函数通过调用前面两个函数实现。

然后实现  加等运算符重载函数 减等运算符重载函数   ,从而实现:加,前置加价,后置加加减,前置减减,后置减减,6个函数通过调用前面两个函数实现。                                  

这里,加等和减等函数都需要函数 GetMonthDay()  得到每个月的天数。

最后,求两个日期相隔多少天的函数,以及重载全局的插入流提取流

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

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

public:
	Date(int year = 1900, int month = 1, int day = 1);

	//括号外面修饰的是 *this
	void Print()const;

	//调用频繁
	//直接定义在类里面,它默认是inline
	int GetMonthDay(int year, int month)
	{
		assert(month > 0 && month < 13);

		static int monthDayArray[13] = { -1, 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;
		}
		else
		{
			return monthDayArray[month];
		}
	}

	//检查日期是否非法
	bool CheckDate();

	bool operator<(const Date& d) const;
	bool operator<=(const Date& d) const;
	bool operator>(const Date& d) const;
	bool operator>=(const Date& d) const;
	bool operator==(const Date& d) const;
	bool operator!=(const Date& d) const;

	//d1 += 100
	Date& operator+=(int day);
	Date operator+(int day) const;

	//d1 -= 100
	Date& operator-=(int day);
	Date operator-(int day) const;

	//内置类型前置和后置区别不大   自定义建议前置的

	//++d1 -> d1.operator++()
	Date& operator++();

	//d1++ -> d1.operator++(0)
	//为了区分,构成重载,给后置++,强行增加了一个int形参
	//这里参数仅仅是为了跟前置++构成重载区分
	Date operator++(int);

	Date& operator--();

	Date operator--(int);

	int operator-(const Date& d) const;

	//流插入
	//不建议,因为Date* this  占据了第一个参数位置,使用的 d<<cout 不符合习惯
	//void operator<<(ostream& out); //他是禁止用拷贝函数的,所以加个引用& 

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

//重载
//操作数的顺序本质是实参的顺序
ostream& operator<<(ostream& out, const Date& d);
istream& operator>>(istream& in, Date& d);
2.3Date.cpp 函数的定义
#include"Date.h"

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

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

	if (!CheckDate())
	{
		cout << "日期非法" << endl;
	}
}
void Date::Print() const
{
	cout << _year << "-" << _month << "-" << _day << endl;
}

// d1 < d2
bool Date::operator<(const Date& d) const
{
	if (_year < d._year)
	{
		return true;
	}
	else if (_year == d._year)
	{
		if (_month < d._month)
		{
			return true;
		}
		else if (_month == d._month)
		{
			return _day < d._day;
		}
	}

	return false;
}

// d1 <= d2
bool Date::operator<=(const Date& d) const
{
	return *this < d || *this == d;
}
bool Date::operator>(const Date& d) const
{
	return !(*this <= d);
}
bool Date::operator>=(const Date& d) const
{
	return !(*this < d);
}
bool Date::operator==(const Date& d) const
{
	return _year == d._year
		&& _month == d._month
		&& _day == d._day;
}
bool Date::operator!=(const Date& d) const
{
	return !(*this == d);
}

// d1 += 50
// d1 += -50
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) const
{
	Date tmp = *this;
	tmp += day;

	return tmp;
}

// d1 -= 100
Date& 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 Date::operator-(int day) const
{
	Date tmp = *this;
	tmp -= day;

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

	return *this;
}
// d1++
Date Date::operator++(int)
{
	Date tmp(*this);
	*this += 1;

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

	return tmp;
}
// d1 - d2 得到多少天数
int Date::operator-(const Date& d) const
{
	Date max = *this;
	Date min = d;
	int flag = 1;
	if (*this < d)
	{
		max = d;
		min = *this;
		flag = -1;
	}

	int n = 0;
	while (min != max)
	{
		++min;
		++n;
	}

	return n * flag;
}
//和习惯上有所不同
//void Date::operator<<(ostream& out)
//{
//	out << _year << "年" << _month << "月" << _day << "日" << endl;
//}
ostream& operator<<(ostream& out, const Date& d)
{
	out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
	return out;
}
istream& operator>>(istream& in, Date& d)
{
	cout << "请依次输入年月日:>";
	in >> d._year >> d._month >> d._day;

	if (!d.CheckDate())
	{
		cout << "日期非法" << endl;
	}

	return in;
}

+= 和 +  两个函数实现思路类似,所以可以任意实现一个,另一个函数调用即可,这里建议实现+=   ,+ 调用 +=   ,减少拷贝构造函数的次数,提高效率

关于日期类实现中前置++,后置++的问题两个点(前置--,后置-- 相同):

1.用参数个数不同区分,函数重载;

2.建议使用 前置++ 。

关于日期类实现中重载流插入,流提取的问题:


3.const成员

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


例子:

const 能加上就加上,使用范围更大。

权限平移,缩小都可以,放大不行。


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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值