<类与对象(下)>——《C++初阶》

本文介绍了C++中的运算符重载,特别是赋值运算符的重载。通过示例解释了如何在自定义类中实现等于(==)和小于(<)运算符,以及如何处理赋值运算符防止自我赋值的问题。同时讨论了const成员函数的作用,强调其在保护对象不被意外修改的重要性。最后提到了取地址及const取地址操作符重载的一般情况。
摘要由CSDN通过智能技术生成

GIF:

目录

5.赋值运算符重载

5.1 运算符重载

5.2 赋值运算符重载 

6.日期类的实现

7.const成员

7.1 const修饰类的成员函数

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

 后记:●由于作者水平有限,文章难免存在谬误之处,敬请读者斧正,俚语成篇,恳望指教!                                                             ——By 作者:新晓·故知


5.赋值运算符重载

5.1 运算符重载

C++为了增强代码的可读性引入了运算符重载运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名字为:关键字operator后面接需要重载的运算符符号
函数原型:返回值类型 operator操作符(参数列表)
注意:
1.不能通过连接其他符号来创建新的操作符:比如operator@
2.重载操作符必须有一个类类型或者枚举类型的操作数
3.内置类型的操作符,其含义不能改变,例如:内置的整型+,不 能改变其含义
4.作为类成员的重载函数时,其形参看起来比操作数数目少1成员函数的
5.符有一个默认的形参this,限定为第一个形参
6.  .* 、:: 、sizeof 、?: 、. 注意以上5个运算符不能重载。这个经常在笔试选择题中出现。
 
// 全局的operator==
class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	//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;
}
void Test()
{
	Date d1(2022, 7, 1);
	Date d2(2022, 7, 10);
	cout << (d1 == d2) << endl;
}
 
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)
	// 这里需要注意的是,左操作数是this指向的调用函数的对象
	bool operator==(const Date& d2)
	{
		return _year == d2._year;
		&& _month == d2._month
			&& _day == d2._day;
	}
private:
	int _year;
	int _month;
	int _day;
};
void Test()
{
	Date d1(2018, 9, 26);
	Date d2(2018, 9, 27);
	cout << (d1 == d2) << endl;
}

 注:函数名为operator,图中operate有误

有多种方式解决:
1.将private改为公有
2.使用公有函数获取  Getyear、Getmonth、Getday

 3.使用友元函数  (破坏封装,不建议采用)

4.将operator函数放在类里面

 注:函数名为operator,图中operate有误

 

 d1默认传给 this,d2传给d

 注:函数名为operator,图中operate有误

//运算符重载
//1.Date类
class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	//运算符重载——函数
	//函数名:operator
	//参数:由运算符操作数决定
	//返回值:运算符运算后结果
	//放在类里面了,这里看起来少了一个参数,是因为有一个this指针
	//放在类里面,解决访问私有数据成员
	bool operator == (const Date& d)
	{
		return _year == d._year
			&& _month == d._month
			&& _day == d._day;
	}
	/*int Getyear()
	{
		return _year;
	}*/
private:
	int _year;
	int _month;
	int _day;
};
运算符重载——函数
函数名:operator
参数:由运算符操作数决定
返回值:运算符运算后结果
//bool operator == (Date d1, Date d2)
//{
//	return d1._year == d2._year
//		&& d1._month == d2._month
//		&& d1._day == d2._day;
//}
int main()
{
	Date d1(2022,07,01);
	Date d2(2022,07,01);
	//1.
	/*if (d1 == d2)
	{
		cout << "==" << endl;
	}*/
	//2.
	/*if ( operator == (d1, d2))   //可以这么使用,但一般不这样写,这相当于调用了这个功能的函数,不像operator函数
	{
		cout << "==" << endl;
	}
	if (d1 == d2)    //编译器会处理成对应重载运算符函数if ( operator == (d1, d2))
	{
		cout << "==" << endl;
	}*/
	//3.
	if (d1. operator == (d2))
	{
		cout << "==" << endl;
	}
	//4.
	if (d1 == d2)   //编译器会处理成对应重载运算符函数if ( d1. operator == (d2))
	{
		cout << "==" << endl;
	}
	return 0;
}

//内置类型,可以直接使用各种运算符
//自定义类型,不能直接用各种运算符,通过运算符重载使其能够使用各种运算符
//这里的重载区别于函数重载的重载

 

 

//运算符重载
//1.Date类
class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	//运算符重载——函数
	//函数名:operator
	//参数:由运算符操作数决定
	//返回值:运算符运算后结果
	//放在类里面了,这里看起来少了一个参数,是因为有一个this指针
	//放在类里面,解决访问私有数据成员
	//A.重载运算符==
	bool operator == (const Date& d)
	{
		return _year == d._year
			&& _month == d._month
			&& _day == d._day;
	}
	/*int Getyear()
	{
		return _year;
	}*/
	//B.重载运算符 <
	bool operator<(const Date& d)
	{
		if ((_year < d._year)
			|| (_year == d._year && _month < d._month)
			|| (_year == d._year && _month == d._month && _day < d._day))
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	//C.重载运算符 >
	bool operator>(const Date& d)
	{
		if ((_year > d._year)
			|| (_year == d._year && _month > d._month)
			|| (_year == d._year && _month == d._month && _day >d._day))
		{
			return true;
		}
		else
		{
			return false;
		}
	}
private:
	int _year;
	int _month;
	int _day;
};
运算符重载——函数
函数名:operator
参数:由运算符操作数决定
返回值:运算符运算后结果
//bool operator == (Date d1, Date d2)
//{
//	return d1._year == d2._year
//		&& d1._month == d2._month
//		&& d1._day == d2._day;
//}
int main()
{
	Date d1(2022,07,03);
	Date d2(2022,07,01);
	//1.
	/*if (d1 == d2)
	{
		cout << "==" << endl;
	}*/
	//2.
	/*if ( operator == (d1, d2))   //可以这么使用,但一般不这样写,这相当于调用了这个功能的函数,不像operator函数
	{
		cout << "==" << endl;
	}
	if (d1 == d2)    //编译器会处理成对应重载运算符函数if ( operator == (d1, d2))
	{
		cout << "==" << endl;
	}*/
	//3.
	/*if (d1. operator == (d2))
	{
		cout << "==" << endl;
	}*/
	//4.实现重载运算符==
	if (d1 == d2)   //编译器会处理成对应重载运算符函数3.if ( d1. operator == (d2))
	{
		cout << "==" << endl;
	}
	//5.实现重载运算符<
	if (d1 < d2)   //编译器会处理成对应重载运算符函数if ( d1. operator < (d2))
	{
		cout << "<" << endl;
	}
	//6.实现重载运算符>
	if (d1 > d2)   //编译器会处理成对应重载运算符函数if ( d1. operator > (d2))
	{
		cout << ">" << endl;
	}
	return 0;
}

内置类型,可以直接用各种运算符

自定义类型,不能直接用各种运算符,通过运算符重载使其能够使用各种运算符

5.2 赋值运算符重载 

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

	Date& operator=(const Date& d)
	{
		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
	}
private:
	int _year;
	int _month;
	int _day;
};

赋值运算符主要有四点:

1. 参数类型
2. 返回值
3. 检测是否自己给自己赋值
4. 返回*this

5. 一个类如果没有显式定义赋值运算符重载,编译器也会生成一个,完成对象按字节序的值拷贝。

 

//运算符重载
//1.Date类
class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
    //赋值重载
	//d2 = d1; ——>编译器会处理成d2.operator=(&d2,d1)
	void operator = (const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1(2022, 07, 01);
	d1.Print();
	Date d2;
	d2.Print();

	Date d3(d1);  //用一个已经存在的对象去初始化另一个要创建的对象——调用拷贝构造函数
	d2 = d1;      //赋值重载/复制拷贝——两个已经存在的对象之间赋值
	d2.Print();
	d3.Print();
 
	return 0;
}

 

 

//运算符重载
//1.Date类
class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	//赋值重载
	//d2 = d1; ——>编译器会处理成d2.operator=(&d2,d1)
	Date& operator = (const Date& d)   //传值返回会生成拷贝等,可以传引用返回Date&
	{
		if (this != &d)  //解决d1=d1,自己给自己赋值
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		return *this;      //this是指针,*this是this指向的对象,是左值
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1(2022, 07, 01);
	d1.Print();
	Date d2;
	d2.Print();

	Date d3(d1);  //用一个已经存在的对象去初始化另一个要创建的对象——调用拷贝构造函数
	d2 = d1;      //赋值重载/复制拷贝——两个已经存在的对象之间赋值
	d2.Print();
	d3.Print();

	在C语言中,赋值的返回值是左值,因为要支持连等
	//int i = 0, j, k;
	//j = i;
	//k = j = i;

	/*Date d3(d1);
	d3 = d2 = d1;
	(d3 = d2) = d1;*/

	d1 = d1;
	d1.Print();
	d2.Print();
	d3.Print();
	return 0;
}

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;
	Date d2(2018,10, 1);

	// 这里d1调用的编译器生成operator=完成拷贝,d2和d1的值也是一样的。
	d1 = d2;
		return 0;
}

那么编译器生成的默认赋值重载函数已经可以完成字节序的值拷贝了,我们还需要自己实现吗?当然像日期类这样的类是没必要的。那么下面的类呢?验证一下试试?
// 这里会发现下面的程序会崩溃掉?这里就需要我们以后讲的深拷贝去解决。
class String
{
public:
	String(const char* str = "")
	{
		_str = (char*)malloc(strlen(str) + 1);
		strcpy(_str, str);
	}
	~String()
	{
		cout << "~String()" << endl;
		free(_str);
	}
private:
		char* _str;
};
int main()
{
	String s1("hello");
	String s2("world");

	s1 = s2;
}
 

6.日期类的实现

 

class Date
{
public:
	// 获取某年某月的天数
	int GetMonthDay(int year, int month)
	{
		static int days[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
		int day = days[month];
		if (month == 2
			&& ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
		{
			day += 1;
		}
		return day;
	}

	// 全缺省的构造函数
	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();
	// 日期+=天数
	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);
	// >=运算符重载
	inline 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);
private:
	int _year;
	int _month;
	int _day;
};

 

7.const成员

7.1 const修饰类的成员函数

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

我们来看看下面的代码

class Date
{
public:
	void Display()
	{
		cout << "Display ()" << endl;
		cout << "year:" << _year << endl;
		cout << "month:" << _month << endl;
		cout << "day:" << _day << endl << endl;
	}
	void Display() const
	{
		cout << "Display () const" << endl;
		cout << "year:" << _year << endl;
		cout << "month:" << _month << endl;
		cout << "day:" << _day << endl << endl;
	}
private:
	int _year; // 年
	int _month; // 月
	int _day; // 日
};
void Test()
{
	Date d1;
	d1.Display();

	const Date d2;
	d2.Display();
}

请思考下面的几个问题:

1. const对象可以调用非const成员函数吗?
解:不能,const不允许权限会放大,
2. 非const对象可以调用const成员函数吗?
解:可以,const允许权限缩小,
3. const成员函数内可以调用其它的非const成员函数吗?
解:成员函数是可以调用成员函数的,但本质上是使用对象或者对象的指针,但const成员函数内不可以调用其它的非const成员函数,因为会权限放大

4. 非const成员函数内可以调用其它的const成员函数吗?
解:可以,

看以下例子:

 

 部分代码:用于讲解演示

//打印函数
//void Print(Date* const this) //const修饰的是this,this本身不能被改
//{
//	cout << _year << "-" << _month << "-" << _day << endl;
//}
//void Print() const  //C++为了解决const权限问题,在函数后面加上const,this就成为了const Date*
等价与void Print(const Date* const this) 
//{				    
//	//编译器会自动加上this,即this->_year等,因此并不是所有函数都可以加,像this指向对象本身不修改的才可以加const
//	//例如:>、<、>=等可以在函数后加const,但++、--、-=、+=等不可以
//	cout << _year << "-" << _month << "-" << _day << endl;
//}
void Func(const Date& d)
{
	d.Print(); //其实是d1.Print(&d); &d取地址的类型是const,即const Date*传给Print的Date*,权限放大
			   //d被const修饰,&d取地址的类型是const Date*,即const 修饰*this
			   //改成void Print() const后,const Date*传给Print的const Date*,权限不变

}		     
void TestDate7()
{
	Date d1(2022, 07, 25);
	d1.Print(); //其实是d1.Print(&d1);&d1取地址的类型是Date*,即Date*传给Print的Date* const,权限缩小
	            //这里&d1传给的this虽然被const修饰,但可以初始化,但this不能被修改
	Func(d1);
}

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

这两个默认成员函数一般不用重新定义 ,编译器默认会生成。
class Date
{ 
public :
 Date* operator&()
 {
 return this ;
 }
 
 const Date* operator&()const
 {
 return this ;
 }
private :
 int _year ; // 年
 int _month ; // 月
 int _day ; // 日
};
这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需要重载,比如不想让别人获取到指定的内容!

 后记:
●由于作者水平有限,文章难免存在谬误之处,敬请读者斧正,俚语成篇,恳望指教!
                                                                     ——By 作者:新晓·故知

  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 12
    评论
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值