C++类与对象(中)赋值运算符重载、const成员、取地址操作符重载

目录

一、赋值运算符重载

1.1运算符重载

注意

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

1.2赋值运算符重载

 小贴士:

1.3前置++和后置++重载

三、const成员

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


一、赋值运算符重载

1.1运算符重载

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

运算符重载写成全局的,就需要成员变量是公有的,为了保证封装性,可直接重载为成员函数。

全局的operator==:

class Date
{
public:
	Date(int year = 2024, int month = 5, int day = 6)
	{
		_year = year;
		_month = month;
		_day = day;
	}
//private:
	int _year;
	int _month;
	int _day;
};

//全局的operator==,此时就需要成员变量是公有的
bool operator==(const Date& d1, const Date& d2)
{
	return d1._year == d2._year
		&& d1._month == d2._month
		&& d1._day == d2._day;
}

int main()
{
	Date d1(2020, 6, 1);
	Date d2(2024, 6, 1);
	cout << (d1 == d2) << endl;
	return 0;
}

重载为成员函数的operator==:
注意:

这里的左操作数是 this ,指向调用函数的对象

bool operator==(Date* this,const Date& d2)

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

	//bool operator==(Date* this,const Date& d2)
	//注意:这里的左操作数是 this ,指向调用函数的对象
	bool operator==(const Date& d)
	{
		return _year == d._year
			&& _month == d._month
			&& _day == d._day;
	}

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

int main()
{
	Date d1(2024, 5, 6);
	Date d2(2024, 7, 6);

	cout << (d1 == d2) << endl;
	return 0;
}

运算符有显式调用和直接调用,当写成显式调用时,就牺牲了代码的可读性,此时与函数的调用类似。 

class Date
{
public:
	Date(int year = 2024, int month = 5, int day = 5)
	{
		this->_year = year;
		this->_month = month;
		this->_day = day;
	}

	//显式调用d1.func(d4);
	//bool func(const Date& d)
	//{
	//	return this->_year == d._year
	//		&& this->_month == d._month
	//		&& this->_day == d._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;
};
int main()
{
	Date d1(2024, 5, 5);
	Date d2(2024, 5, 6);

	//显式调用
	d1.operator==(d2);

	//转换调用,等价于d1.operator==(d2),底层实现一样
	d1 == d2;

	return 0;
}

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

易混淆:

拷贝构造:一个已经存在的对象,拷贝给另一个要创建初始化的对象

赋值拷贝/赋值重载:一个已经存在的对象,拷贝赋值给另一个已经存在的对象

1.2赋值运算符重载

(1)赋值运算符重载格式

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

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

  <3>检查是否自己给自己赋值,避免损失

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

当是两个数之间的赋值时,可直接返回 void ,若是三个数或者三个数以上,则不可以。

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

	void operator=( const Date& d)//无返回值
	{
		_year = d._year;
		_month = d._month;
		 _day = d._day;
	}

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

int main()
{
	Date d1(2024, 5, 6);
	Date d2(d1);
	Date d3 = d2;

	d1 = d2 = d3;//error
    
	return 0;
}

 

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

   //d1 = d2 = d3
	Date& operator=( const Date& d)
	{
		if (this!= &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 = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
//private:
	int _year;
	int _month;
	int _day;
};

//重载成全局函数,此时就没有this指针了,需要给两个参数
Date& operator=(Date& left,const Date& right)//error:“operator =”必须是非静态成员
{
	if (&left != &right)
	{
		left._year = right._year;
		left._month = right._month;
		left._day = right._day;
	}
	return left;
}
//error:

int main()
{
	Date d1(2024, 5, 6);
	Date d2(2024, 7, 6);
	Date d3(2025, 8, 6);
	d1 = d2 = d3;

	return 0;
}

传值返回和引用返回的区别:(在效率上的区别)

传值:返回拷贝

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

	Date(const Date& d)
	{
		cout << "Date(const Date& d)" << endl;
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
private:
	int _year;
	int _month;
	int _day;
};

Date func()//传值返回--->拷贝一个临时对象
{
	Date d;
	return d;
}

int main()
{
	func();
	return 0;
}

引用:返回别名

 小结:

返回对象是一个局部对象或者临时对象,出了当前func()函数作用域,就析构销毁了,那么不能引用返回,用引用返回是存在风险的,因为引用对象在func()函数栈帧已经销毁了,

虽然返回引用可以减少一次拷贝,但是出了函数作用域,返回对象还在,才能引用返回。

 小贴士:

出了作用域,返回对象还在没有析构,那就可以引用返回,减少拷贝

a.返回对象生命周期到了,会析构,传值返回

b.返回对象生命周期没到,不会析构,传引用返回

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

     注意:内置类型成员是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。(跟拷贝构造函数类似,Date MyQueue 默认生成的赋值[浅拷贝/值拷贝][未涉及到资源管理],但是类似Stack/List[深拷贝][涉及到资源管理] 等需要我们自己实现赋值重载)

1.3前置++和后置++重载

++d :返回++以后的值

d++ :返回++之前的值

函数重载和运算符重载的区别:

函数重载:允许函数名相同,参数不同的函数存在

运算符重载:让自定义类型可以用运算符,并且控制运算符的行为,增强可读性

它们之间各论各的,没有关系,多个同一运算符重载可以构成函数重载

 前置++和后置++都是单操作数,此时该怎么办呢?

(规定)强行给后置++增加一个形参,这里不需要写形参名,因为接收值是多少不重要,也不需要用,这个参数仅仅是为了跟前置++构成重载区分。

两个逻辑颠倒,会导致逻辑错误,不会报错,但跟常规设定不符。

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

	++前置
	Date& operator++()
	{
		_day += 1;
		return *this;
	}

	后置++
	C++规定:后置++重载时多增加一个int类型的参数,
	但是调用函数时该参数不用传递,编译器自动传递
	Date operator++(int)
	{
		Date temp(*this);  后置++是先使用后+1,因此需要返回+1之前的旧值,
		                   故需在实现时需要先将this保存一份,然后再给this+1
		_day += 1;
		return temp;  tmp是临时对象,因此只能以值的方式返回,不能返回引用
	}

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

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

int main()
{
	Date d1(2024, 5, 7);
	Date d2 = ++d1;
	d1.Print();//2024-5-8
	d2.Print();//2024-5-8  返回+1后的值

	Date d3 = d1++;
	d1.Print();//2024-5-9
	d3.Print();//2024-5-8  返回+1前的值

	return 0;
}

三、const成员

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

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

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

class A
{
public:
	//取地址操作符重载
	A* operator&()
	{
		return this;
	}
	//const取地址操作符重载
	const A* operator&() const
	{
		return this;
	}
private:
	int _a = 1;
};

int main()
{
	A a1;
	const A a2;

	cout << &a1 << endl;
	cout << &a2 << endl;
	return 0;
}

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

如若对你有帮助,记得点赞、收藏、关注哦!

若有误,望各位,在评论区留言或者私信我 指点迷津!!!谢谢^ ^ ~

  • 32
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值