C++类和对象中下篇

赋值运算符重载

运算符重载

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

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

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

注意:

1.不能通过连接其他符号来创建新的操作符:比如operator@

2.重载操作符必须有一个类类型参数

3.用于内置类型的操作符,其含义不能改变,例如:内置的整型+,不能改变其含义。但是这点指数相对的规定,比如说你想恶搞别人,就把符号写反也是可以的。但最好不要这样做,要不然,免不了被一顿骂。

4.作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this

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

对.*的解释

// 全局的operator==
class Date
{ 
public:
 Date(int year = 2024, int month = 4, int day = 14)
   {
        _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(2024, 4, 13);
    Date d2(2024, 4, 13);
    cout<<(d1 == d2)<<endl;
}

 解决方法:重载为成员函数

class Date
{
public:
	Date(int year = 1111, int month = 11, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	bool operator==(const Date& d)
	{
		return this->_year == d._year
			&& this->_month == d._month
			&& this->_day == d._day;
	}

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

赋值运算符重载

1.赋值运算符重载格式

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

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

检测是否自己给自己赋值

返回*this

class Date
{
public:
	Date(int year = 1111, int month = 11, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

	bool operator==(const Date& d)
	{
		return this->_year == d._year
			&& this->_month == d._month
			&& this->_day == d._day;
	}

    //这里返回值不是void针对的是多次赋值,比如d1=d2=d3
	//这里然后就用传引用了,如果用传值的话会返回临时变量,即多进行了拷贝,这从效率的角度上来说
	//是不太好的
	Date operator=(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;

		return *this;
	}
private:
	int _year;
	int _month;
	int _day;
};

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

class Date
{
public:
 Date(int year = 1900, int month = 1, int day = 1)
 {
 _year = year;
 _month = month;
 _day = day;
 }
 int _year;
 int _month;
 int _day;
};
// 赋值运算符重载成全局函数,注意重载成全局函数时没有this指针了,需要给两个参数
Date& operator=(Date& left, const Date& right)
{
 if (&left != &right)
 {
 left._year = right._year;
 left._month = right._month;
 left._day = right._day;
 }
 return left;
}
// 编译失败:
// error C2801: “operator =”必须是非静态成员

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

3. 用户没有显式实现是,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。注意:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。

class Time
{
public:
	Time()
	{
		_hour = 1;
		_minute = 1;
		_second = 1;
	}
	Time& operator=(const Time& t)
	{
		if (this != &t)
		{
			_hour = t._hour;
			_minute = t._minute;
			_second = t._second;
		}
		return *this;
	}
private:
	int _hour;
	int _minute;
	int _second;
};
class Date
{
private:
	// 基本类型(内置类型)
	int _year = 2024;
	int _month = 4;
	int _day = 14;
	// 自定义类型
	Time _t;
};
int main()
{
	Date d1;
	Date d2;
	d1 = d2;
	return 0;
}

既然编译器生成的默认赋值运算符重载函数已经可以完成字节序的值拷贝了,还需要自己实现吗?

当然像日期类这样的类是没有必要的。那么下面的类呢?

// 这里会发现下面的程序会崩溃掉?这里就需要我们以后讲的深拷贝去解决。
typedef int DataType;
class Stack
{
public:
	Stack(size_t capacity = 10)
	{
		_array = (DataType*)malloc(capacity * sizeof(DataType));
		if (nullptr == _array)
		{
			perror("malloc申请空间失败");
			return;
		}
		_size = 0;
		_capacity = capacity;
	}
	void Push(const DataType& data)
	{
		// CheckCapacity();
		_array[_size] = data;
		_size++;
	}
	~Stack()
	{
		if (_array)
		{
			free(_array);
			_array = nullptr;
			_capacity = 0;
			_size = 0;
		}
	}
private:
	DataType* _array;
	size_t _size;
	size_t _capacity;
};
int main()
{
	Stack s1;
	s1.Push(1);
	s1.Push(2);
	s1.Push(3);
	s1.Push(4);
	Stack s2;
	s2 = s1;
	return 0;
}

注意:如果类中没有涉及到资源管理,赋值运算符是否实现都可以;一旦涉及到资源管理则必须要实现。

这里的解决方法 就是实现深拷贝,深拷贝就是开跟s1一样大的空间给s2.

前置++和后置++重载

class Date
{
public:
	Date(int year = 2024, int month = 4, int day = 14)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	// 前置++:返回+1之后的结果
	// 注意:this指向的对象函数结束后不会销毁,故以引用方式返回提高效率
	Date& operator++()
	{
		_day += 1;
		return *this;
	}
	// 后置++:
	// 前置++和后置++都是一元运算符,为了让前置++与后置++形成能正确重载
	// C++规定:后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器自动传递
		// 注意:后置++是先使用后+1,因此需要返回+1之前的旧值,故需在实现时需要先将this保存一份,然后给this + 1
		//而temp是临时对象,因此只能以值的方式返回,不能返回引用
		Date operator++(int)
	{
		Date temp(*this);
		_day += 1;
		return temp;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d;
	Date d1(2022, 1, 13);
	d = d1++;    // d: 2022,1,13   d1:2022,1,14
	d = ++d1;    // d: 2022,1,15   d1:2022,1,15
	return 0;
}

日期类的实现

日期类的类

//Date.h
#pragma once
#include<iostream>
using namespace std;

#include<assert.h>
//日期类
class Date
{
	//友元函数声明
	friend ostream& operator<<(ostream& out, const Date& d);
	friend istream& operator>>(istream& in, Date& d);

public:
	//构造
	Date(int year = 2024, int month = 4, int day = 1);
	//打印
	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;


	//重载日期加等天数
	Date operator+=(int day);
	//重载日期加天数
	Date operator+(int day) const;
	//重载日期减等天数
	Date operator-=(int day);
	//重载日期减天数
	Date operator-(int day) const;

	//重载日期减日期
	int operator-(const Date& d) const;

	//重载前置加加
	Date& operator++();
	//重载后置加加,这里的传的参数没什么用,只是语法上为了区分。
	Date operator++(int);

	//重载前置减减
	Date& operator--();
	//重载后置减减
	Date operator--(int);

	//流插入
	// 不建议,因为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);

//Date.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;
}

//这样部分,完成两个,其他的就结合起来或者取反就可以实现了
	//重载小于
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;
}
//重载小于等于
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);
}

//重载日期加等天数
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;
}
//重载日期减等天数
Date 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);
	}

}
//重载日期减天数
Date Date::operator-(int day) const
{
	Date tmp = *this;
	tmp -= day;

	return tmp;
}

//重载日期减日期
int Date::operator-(const Date& d) const
{
	Date max = *this;
	Date min = d;
	int flag = 1;
	if (max <= min)
	{
		max = d;
		min = *this;
		flag = -1;
	}

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

	return n * flag;
}

//重载前置加加
Date& Date::operator++()
{
	*this += 1;
	return *this;
}
//重载后置加加,这里的传的参数没什么用,只是语法上为了区分。
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;
}


//重载
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;
}

//Test.cpp

#include"Date.h"

void TestDate1()
{
	Date d1(2024, 4, 14);
	Date d2 = d1 + 30000;
	d1.Print();
	d2.Print();

	Date d3(2024, 4, 14);
	Date d4 = d3 - 5000;
	d3.Print();
	d4.Print();

	Date d5(2024, 4, 14);
	d5 += -5000;
	d5.Print();
}

void TestDate2()
{
	Date d1(2024, 4, 14);
	Date d2 = ++d1;
	d1.Print();
	d2.Print();

	Date d3 = d1++;
	d1.Print();
	d3.Print();

	/*d1.operator++(1);
	d1.operator++(100);
	d1.operator++(0);
	d1.Print();*/
}

void TestDate3()
{
	Date d1(2024, 4, 14);
	Date d2(2034, 4, 14);

	int n = d1 - d2;
	cout << n << endl;

	n = d2 - d1;
}

void TestDate4()
{
	Date d1(2024, 4, 14);
	Date d2 = d1 + 30000;

	// operator<<(cout, d1)
	cout << d1;
	cout << d2;

	cin >> d1 >> d2;
	cout << d1 << d2;
}

void TestDate5()
{
	const Date d1(2024, 4, 14);
	d1.Print();

	//d1 += 100;
	d1 + 100;

	Date d2(2024, 4, 25);
	d2.Print();

	d2 += 100;

	d1 < d2;
	d2 < d1;
}

int main()
{
	// 提高效率输入的效率,竞赛
	/*ios_base::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);*/

	TestDate4();
	//TestDate5();

	//测试两日期比较
	//Date d1(2024,8,8);
	//d1.Print();
	//Date d2(2024, 1, 1);
	//bool a=d1 < d2;
	//bool b = d1 == d2;
	//cout << a <<" "<<b<<endl;
	//bool c = d1 != d2;
	//cout << c << endl;

	/*Date d3(2024, 1, 1);
	d3 += 31;
	d3.Print();

	Date d4(2024, 1, 1);
	d4 -= 1;
	d4.Print()*/;

	return 0;
}

const成员

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

 

#include<iostream>
using namespace std;
class Date
{
public:
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << "Print()" << endl;
		cout << "year:" << _year << endl;
		cout << "month:" << _month << endl;
		cout << "day:" << _day << endl << endl;
	}
	void Print() const
	{
		cout << "Print()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(2022, 1, 13);
	d1.Print();
	const Date d2(2022, 1, 13);
	d2.Print();
}

几个问题:

1.const对象可以调用非const成员函数吗?

2.非const对象可以调用const成员函数吗?

3.const成员函数内可以调用其他的非const成员函数吗?

4.非const成员函数内可以调用其他的const成员函数吗?

答:const修饰后的对象权限是较小的,未被修饰的权限较大

1.const对象可以调用const成员函数,const对象不能调用非const成员函数
2.非const对象可以调用const成员函数,非const对象可以调用非const成员函数
3.const成员函数不能调用非const成员函数
4.非const成员函数可以调用非const成员函数

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

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

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

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

  • 17
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值