类和对象的深入了解5

1.运算符重载

C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类 型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。 函数名字为:关键字operator后面接需要重载的运算符符号。 函数原型:返回值类型 operator操作符(参数列表)

注意:

不能通过连接其他符号来创建新的操作符:比如operator@ 重载操作符必须有一个类类型参数 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this。

.* :: sizeof ?: . 注意以上5个运算符不能重载。这个经常在笔试选择题中出现。

2.赋值运算符重载

1.参数类型:const T&,传递引用可以提高传参效率

2.返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值

3.检测是否自己给自己赋值

4.返回*this :要复合连续赋值的含义

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

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

6.用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。注意:内置类 型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。(如果类中未涉及到资源管理,赋值运算符是否实现都可以;一旦涉及到资源管理则必须要实现。也就是深拷贝的样式

#include<iostream>
using namespace std;

class MyClass {
private:
   int data;
public:
   MyClass(int d = 0) { data = d; }

   // 重载赋值运算符
   MyClass& operator=(const MyClass& other) {
      // 检查自身赋值
      if (this == &other) {
         return *this;
      }
      // 执行赋值操作
      data = other.data;
      return *this;
   }
};

int main() {
   MyClass obj1(10);
   MyClass obj2(20);

   // 使用赋值运算符重载将obj2的值赋给obj1
   obj1 = obj2;

   return 0;
}
 

在上面的示例中,我们定义了一个名为MyClass的类,并重载了赋值运算符"="。在main函数中,我们创建了两个MyClass对象obj1obj2,并使用重载的赋值运算符将obj2的值赋给obj1

3.完整Date类的运算符重载

3.1.cpp文件
#include"Date.h"


bool Date::ChekDate()
{
	if (_month < 1 || _month>12 || _day<1 || _day>Get_month_day(_year, _month)) {
		return false;
	}
	else {
		return true;
	}
}
Date::Date(int year,int month,int day) {
	_year = year;
	_month = month;
	_day = day;
	if (!ChekDate()) {
		cout << "日期非法!" << endl;
		return;
	}
}

void Date:: print ()const
{
	cout << _year << "_" << _month << "_" << _day << endl;
}

bool Date::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;
			}
		}
	}
	else {
		return false;
	}
}
bool Date::operator==(const Date& d) {
	return _year == d._year && _month == d._month && _day == d._day;
}
bool Date::operator<=(const Date& d) {
	return *this < d || *this == d;
}
bool Date::operator>(const Date& d) {
	return !(*this <= d);
}
bool Date::operator>=(const Date& d) {
	return !(*this < d);
}

bool Date::operator!=(const Date& d) {
	return !(*this == d);
}

//d1+=50
 Date& Date:: operator+=(int day) {
	_day += day;
	while (_day > Date::Get_month_day(_year, _month)) {
		_day -= Get_month_day(_year, _month);
		++_month;
		if (_month == 13) {
			++_year;
			_month = 1;
		}
	}
	return *this;
}
 Date Date::operator+(int day) {
	 Date tmp = *this;//拷贝构造
	 tmp += day;
	 return tmp;
	 //由于tmp为局部对象,返回后将销毁,若还是用&返回的话
	 //那么将会得到随机值,所以这里必须要拷贝构造成临时对象进行返回。

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

Date Date::operator-(int day) {
	Date tmp = *this;//拷贝构造
	tmp -= day;
	return tmp;
}
//前置++
Date& Date::operator++() {
	*this += 1;
	return *this;
}
//后置++,祖师爷为了区分这俩的区别,给后置++给了一个int类型的形参进行区分
Date Date::operator++(int) {
	Date tmp = *this;
	*this += 1;
	return tmp;//对于局部变量,必须使用拷贝构造出临时对象进行返回。
}

int Date:: operator-(const Date& d) {
	
	Date max = *this;
	Date min = d;
	int flag = 1;
	if (max < min) {
		max = d;
		min = *this;
		flag = -1;
	}
	int n = 0;
	while (max != min) {
		++min;
		++n;
	}

	return n * flag;//用flag来化解大小问题
}
3.2.h文件
#pragma once
#include<iostream>
#include<assert.h>
#include<stdbool.h>
using namespace std;

class Date {
public:
	bool ChekDate();//用于检查构造出的日期是否非法。


	Date(int year = 1990, int month = 1, int day = 1);
	void print()const;
	bool operator<(const Date& d);//比较的参数原本是两个,但是这里有一个this指针,
	bool operator<=(const Date& d);//所以只需要传一个参数。
	bool operator>(const Date& d);//参数至少要有一个是类类型。
	bool operator>=(const Date& d);//用引用作为形参可以减少拷贝构造的消耗
	bool operator==(const Date& d);//const持平权限.临时变量具有常性
	bool operator!=(const Date& d);
	//直接定义在类中的函数,就是内联函数(inline),不会进符号表。
	//这个函数会被频繁调用
	int Get_month_day(int year,int month) {
		assert(_month > 0 && _month < 13);
		//由于每次调用都会创建该数组,所以我们可以将其改为静态,这样就可以减少创建的消耗。
		//静态数组在整个程序结束后才会销毁,这样每次调用该函数就不需要创建数组所需要的消耗。
		static int Get_month_day_arry[] = { -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 Get_month_day_arry[month];
		}
	}
//日期与天数
	Date& operator+=(int day);
	Date operator+(int day);
	Date& operator-=(int day);
	Date operator-(int day);

	//前置++和后置++重载
	Date& operator++();
	Date operator++(int);
 
	//日期减日期
	int operator-(const Date& d);


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

前置++和后置++重载我们的前辈为了区别这俩,就给后置++增添了一个int类型参数,在上述代码中也有注释,供大家参考。

4.const成员

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

 当我们创建一个const修饰对象时,然后调用成员函数时,将对像的地址传给成员函数,也就是this指针来接收,this的完全形式是类型+const *this,我们知道这样只是限制了this,并没有限制*this,所以我们将成员函数的this也加上const,就有了上述的操作,这里其实是权限放大的问题,用const达到权限平衡。

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

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

class Date
{ 
public :
 Date* operator&()
 {
 return this ;
 }
 
 const Date* operator&()const
 {
 return this ;
 }
private :
 int _year ; // 年
 int _month ; // 月
 int _day ; // 日
};

这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需要重载,比如不想让别人获取到指定的内容!

谢谢
  • 28
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

c23856

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值