C++学习之运算符重载

运算符重载也可以发生函数重载,多个同名函数形参不同实现不同的功能,这些同名函数的形参列表(参数个数,类型,顺序)必须不同,常用来处理实现功能类似数据类型不同的问题。

加号重载

成员函数重载+号

#include <iostream>
#include<string>
using namespace std;

class Person
{
public:
	int m_a;
	int m_b;
	//重载函数
	Person operator+(Person& a)
	{
		Person temp(this->m_a + a.m_a, this->m_b + a.m_b);
		return temp;
	}
	Person(int a, int b)
	{
		this->m_a = a;
		this->m_b = b;
	}
};
void test()
{
	Person p1(10,10);
	Person p2(1,2);
	Person p3 = p1 + p2;
	cout << "p3.m_a=" << p3.m_a << endl;
	cout << "p3.m_b=" << p3.m_b << endl;
}
int main()
{
	test();
	system("pause");
}

成员函数重载本质

Person p3 = p1.operator+(p2);

全局函数重载

Person operator+(Person& a,Person& b)
{
	Person temp(a.m_a+b.m_a, a.m_b + b.m_b);
	return temp;
}

全局函数重载的本质:

Person p3 = operator+(p1,p2);

左移运算符重载

可以输出想要的格式
//如果用成员函数重载左移运算符
//最终重载后:p.operator<<(cout),简化后 p<<cout
//和常用的顺序不一样,所以一般不用成员函数重载左移运算符
void operator<<(cout)
{}

void operator<<(ostream &cout,Person &p)//本质:operator<<(cout,p), 简化后cout<<p
{
	cout << "m_a=" << p.m_a << endl << "m_b=" << p.m_b << endl;
}
void test()
{
	Person p1(10,10);
	Person p2(1,2);
	cout << p1 ;
}

此时

cout << p1 ;

这后面不能再<<加其他东西,因为重载函数返回是void,而想要无限追加是运用了链式编程,返回是void就不能继续调用void operator<<(ostream &cout,Person &p),而返回值是ostream &cout就可以继续追加

ostream &operator<<(ostream &cout,Person &p)//本质:operator<<(cout,p), 简化后cout<<p
{
	cout << "m_a=" << p.m_a << endl << "m_b=" << p.m_b << endl;
	return cout;=
}
void test()
{
	Person p1(10,10);
	Person p2(1,2);
	cout << p1<<p2 ;
}

当全局函数访问类中的私有属性时,可以引入友元

递增运算符重载

++a和a++

	int a = 10;
	cout << "a=" << a << endl;
	cout << "++a+2=" << ++a+2 << endl;
	cout << "a=" << a << endl;
	int b = 10;
	cout << "b=" << b << endl;
	cout << "2+b++=" << 2+b++ << endl;
	cout << "b=" << b << endl;

在这里插入图片描述

前置递增,先变量递增,再输出表达式,++a+2就是a=a+1先递增变成11,再计算表达式11+2=13,变量值是在语句被求值之前改变的
后置递增,先表达式运算,再变量递增
2+b++,先计算2+b=12,再b递增,b=b+1

前置递增

写在成员函数里

	//前置递增
	Person  & operator++()
	{
		//先做++运算
		 this->m_a++;
		 //再返回自身做其他表达式运算
		 return *this;
	}

跟着老师编写代码时的疑点:
1、这样写在成员函数中的话经过简化后不应该是p++吗?
介绍一下operator函数
格式:<返回类型说明符> operator <运算符符号>(<参数表>)
成员函数运算符重载时:
(1) 二元运算符重载为类的成员函数时,函数只显式说明一个参数,该形参是运算符的右操作数。p.operator运算符(形参a)简化后:p运算符a
(2) 前置一元运算符重载为类的成员函数时,不需要显式说明参数,即函数没有形参。p.operator运算符()简化后:运算符p,此处就是前置。
(3) 后置一元运算符重载为类的成员函数时,函数要带有一个整型形参。目的:前置后置函数名一样,这里用到函数重载,函数重载的要求是参数顺序,个数,类型不同。
全局函数重载:
operator运算符(形参a,形参b) 简化后:a运算符b
2、为什么 Person & operator++()要加&符号,不加会报错
因为:

int main()
{
	//test();
	int a = 1;
	cout << "++(++a)=" << ++(++a) << endl;
	cout << "a=" << a << endl;
	system("pause");
}

&符号即引用,起标识符作用,相当于对一个变量起别名
不使用&时,Person operator++()这里直接返回值的话相当于使用拷贝构造函数返回了一个新的对象(返回值调用拷贝构造),使用引用返回相当于返回这个对象本身。
如果不使用&,++p也不报错,那么,像++(++p)这种,p的值就只加了一次,而原本的前置递增++(++a)是对一个数据进行操作。返回引用是为了一直对一个数据进行操作。

后置递增

	//后置递增  int代表占位参数,可以用于区分前置和后置
	Person operator++( int )
	{
		//先返回原来的值做其他表达式运算
		Person temp = *this;
		this->m_a++;
		//再返回自身
		return temp;
	}

疑问:
1、为什么后置不加&,
因为后置使用的局部变量temp,在函数执行之后内存就被释放了,这时再&,找不到地址就会出现错误。
2、为什么上面后置这么写

	cout <<  p2++ << endl;

会报<<的错。
因为之前<<重载函数定义为ostream &operator<<(ostream &cout,Person &p)
第二个参数是引用的形式传入,而后置重构返回的temp的值,再被<<输入时,只能是值的状态。因为执行完++时temp已经被释放没有内存空间,也就不能产生同地址的引用。
解决方法:
(1)<<重载函数传的是引用类型,把引用类型改为传值就好了ostream& operator<<(ostream& cout, Person p)
(2)在<<重载的时候将myint输入参数加上const ostream& operator<<(ostream& cout, const Person & p)
(3)想解决<<报错还不想该<<重载的可以将temp开辟在堆里,返回值仍然可以用引用 ,但是要记得析构释放堆区内存。但本人对内存仍一知半解,试了几次没成功,下面是我的代码,仍然报错,析构的代码也不太会。

Person operator++( int )
{
	//先返回原来的值做其他表达式运算
	Person *temp = new Person();
	temp->m_a = this->m_a;
	this->m_a++;
	//再返回自身
	return *temp;
}

后置++操作正常是先引用后递增,所以这里用了一个temp来记录递增之前的值,而不是直接返回原来的数的引用,但这里确实不可以进行链式操作了,因为返回回来的对象不是原来的对象,返回的对象是temp,

赋值运算符重载

C++至少给一个类添加4个函数:默认无参、有参、拷贝构造和赋值运算符operator=,对属性进行值拷贝,如果类中有属性指向堆区,做赋值操作时也会出现深浅拷贝问题。
首先在堆区创建变量,然后输出

class Person
{
public:
	int* m_age;//创建一个指针,年龄真实的数据存在堆区
	Person(int age)//传进的年龄
	{
		m_age = new int(age);//把传进的年龄开辟到堆区,用指针来维护这个数据
	//指针来管理这块内存
	}
};
void test()
{
	Person p1(18);
	cout << "p1的年龄:" << p1.m_age << endl;//获取了指针的地址
	cout << "p1的年龄:" << *p1.m_age << endl;//获取内容
}

&:取址运算符,获取某个变量的地址,int* pb = &f;
:可以获取指针变量指向的数据,也叫解引用操作符,定义指针时也用到了,这里属于符号的「重用」,也就是说这种符号在不同的地方就有不同的用意:在定义的时候表示「定义一个指针变量」,在其他的时候则用来「获取指针变量指向的变量的值」。
对堆区数据进行赋值会出现浅拷贝的问题:

void test()
{
	Person p1(18);
	Person p2(20);
	cout << "赋值前:p1的年龄地址:" << p1.m_age << endl;//获取了指针的地址
	cout << "p2的年龄地址:" << p2.m_age << endl;//获取了指针的地址
	p1 = p2;
	cout << "赋值后:p1的年龄地址:" << p1.m_age << endl;//获取了指针的地址
	cout << "p2的年龄地址:" << p2.m_age << endl;//获取了指针的地址
	cout << "p1的年龄:" << *p1.m_age << endl;//获取内容
	cout << "p2的年龄:" << *p2.m_age << endl;
}

在这里插入图片描述
可以看出:赋值前p1和p2指针地址不同,赋值后就是把两个对象的年龄指针指向同一块内存。
问题出现了,由于在堆区开辟的数据需要程序员手动开辟手动释放,所以需要用户自己提供析构函数

深拷贝:

~Person()
{
	if(m_age != NULL)
	{
		delete m_age;
		m_age = NULL;//置空,防止野指针的出现
	}
	cout << "析构函数调用"<<endl;
}
void operator=(Person& p)
{
	//编译器提供的浅拷贝是 this->m_age=p.m_age
	//采用深拷贝,p2赋值给p1,p2的指针指向不动,
	//给p1重新开辟一块空间,若p1原来指针指向有东西,得先置空、
	if (m_age != NULL)
	{
		delete m_age;
		m_age = NULL;
	}
	m_age = new int(*p.m_age);
}

输出后
在这里插入图片描述
看出,赋值后p1和p2地址不同
但仍旧有一些问题,正常情况下的赋值=可以进行链式编程,如:

int a = 10;
int b = 10;
int c = 10;
c = b = a;
cout << "a=" << a << endl << "b=" << b << endl << "c=" << c << endl;
system("pause");

输出a=10,b=10,c=10
所以

Person & operator=(Person& p)
{
	//编译器提供的浅拷贝是 this->m_age=p.m_age
	//采用深拷贝,p2赋值给p1,p2的指针指向不动,
	//给p1重新开辟一块空间,若p1原来指针指向有东西,得先置空、
	if (m_age != NULL)
	{
		delete m_age;
		m_age = NULL;
	}
	m_age = new int(*p.m_age);
	return *this;
}

输出在这里插入图片描述

Person operator=(Person& p)是返回值,是创建一个副本,不是返回自身,所以用加引用 &。
和前面说不能返回值的原因有点偏差,首先是返回值会调用默认构造,造成浅拷贝,自己写一个深拷贝也出错原因是,因为赋值符号发生改变没有左值,p2=p1只是作为一个右值,是一个特殊的类,会立马析构。

关系运算符重载

大于等于小于不等这些符号,
以大于为例

	bool operator>(Person& p)
	{
		if (m_age > p.m_age)
		{
			return true;
		}
		else
			return false;
	}

但我想不到怎么处理链式编程,返回值是布尔类型不能和Person进行比较,不过好像原来的关系运算符就不能链式编程(此处存疑)

函数调用运算符重载

()小括号,由于重载后使用的方式非常像函数的调用,因此称为仿函数,仿函数没有固定写法,非常灵活。

class Person
{
public:
	int m_age;
	int operator()(int num1,int num2)
	{
		return num1 + num2;
	}
};
void test()
{
	Person p1;
	cout<<p1(1,2)<<endl;
}

匿名函数对象

cout << Person()(1,2) << endl;

首先Person()是一个匿名对象,类型加小括号,特点:当前行执行完立即释放,后面的小括号是重载的运算符。

  • 11
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值