【学习篇】【C++】【类和对象】【运算符重载】

一、加号+ 运算符重载

实现两个自定义数据类型相加的运算


1.成员函数实现+号运算符重载

Person operator+(const Person& p)
{
	Person temp;
	temp.m_a = this->m_a + p.m_a;
	temp.m_b = this->m_b + p.m_b;
	return temp;
}

此时可以写出Person p3 = p1 + p2,但其实本质是Person p3 = p1.operator+(p2)


2.全局函数实现+号运算符重载

Person operator+(const Person& p1, const Person& p2)
{
	Person temp(0, 0);
	temp.m_a = p1.m_a + p2.m_a;
	temp.m_b = p1.m_b + p2.m_b;
	return temp;
}

此时可以写出Person p3 = p1 + p2,但其实本质是Person p3 = operator+(p1, p2)


3.运算符重载后的函数重载(全局下)

Person operator+(const Person& p1, int num)
{
	Person temp(0, 0);
	temp.m_a = p1.m_a + 100;
	temp.m_b = p1.m_b + 100;
	return temp;
}

此时可以写出Person p4 = p1 + 100 ,但其实本质是Person p4 = operator+(p1, 100)

【总结】

  1. 对于内置的数据类型的表达式,运算符是不可以改变的。
  2. 不要滥用运算符重载

二、左移<< 运算符重载

可以输出自定义的数据类型

class Person
{
	friend ostream& operator<<(ostream& out, Person& p);
private:
	int m_a;
	int m_b;
public:
	Person(int a, int b)
	{
		this->m_a = a;
		this->m_b = b;
	}
};

不能利用成员函数来重载左移运算符,否则会变成p.operator<<(cout)等同于p << cout。因为无法实现cout在左侧。所以只能利用全局函数重载左移运算符

ostream& operator<<(ostream& out, Person& p)//简化为cout << p
{
	out << "m_a = " << p.m_a << " m_b =  " << p.m_b;
	return out;
}

void test01()
{
	Person p1(10, 10);
	cout << p1 << "hello world " << endl;
}

特别注意这一行代码:

cout << p1 << "hello world" << endl;

此处当cout << p1执行完后,返回的是cout,因此此时代码等同于cout << "hello world" << endl;。此时因为 "hello world"endl均不是Person类的对象,因此接下来调用的运算符<<都是编辑器初始的左移运算符,而不是我们自定义重载后的。

【总结】
1. 重载左移运算符配合友元可以实现输出自定义数据类型

friend ostream& operator<<(ostream& out, Person& p);

三、递增++ 运算符重载

实现自己的整型数据


此处得先定义左移运算符,便于后续的输出操作。

class MyInterger
{
	friend ostream& operator<<(ostream& cout, MyInterger myint);
private:
	int m_num;
public:
	MyInterger()
	{
		m_num = 0;
	}
};
//重载左移<<运算符
ostream& operator<<(ostream& cout, MyInterger myint)
{
	cout << myint.m_num;
	return cout;
}

1.前置递增

//1.重载递增++运算符(前置递增)
MyInterger& operator++()
//此处返回“引用”是保证后续的操作都是作用在同一个对象上。
{
	m_num++;//先进行++运算
	return *this;//再将自身做返回
}
  1. 此处返回“引用”是保证后续的操作都是作用在同一个对象上。

2.后置递增

//2.重载递增++运算符(后置递增)
//int表示占位参数,可以用于区分前置递增和后置递增
MyInterger operator++(int)
{
	//先返回结果(具体操作:先记录当时结果)
	MyInterger temp = *this;
	//后递增
	m_num++;
	//最后将记录结果做返回
	return temp;
	//此处返回的是指针而不是引用,因为temp会在函数执行完后就被释放掉,此时再返回temp的引用就是非法操作了
}
  1. 此处int表示占位参数,可以用于区分前置递增和后置递增
  2. 此处返回的是指针而不是引用,因为temp会在函数执行完后就被释放掉,此时再返回temp的引用就是非法操作了

3.编译结果分析

void test01()
{
	MyInterger MyInt;
	cout << MyInt << endl;
	cout << ++MyInt << endl;
	cout << MyInt << endl;
}

void test02()
{
	MyInterger MyInt;
	cout << MyInt << endl;
	cout << MyInt++ << endl;
	cout << MyInt << endl;
}

test01的输出结果为:0 1 1。test02的输出结果为:0 0 1。原因是重载后的前置递增是先进行++运算,再将自身做返回。而后置递增是先记录初始结果,再进行++运算,最后将初始结果做返回。
在这里插入图片描述

四、赋值= 运算符重载

c++编辑器至少给一个类添加4个函数:
1~3.默认的(构造函数,析构函数,拷贝构造函数)
4.赋值运算符operator=的对属性进行值拷贝

举例:
我们通过重载后的赋值=运算符将一个对象的所有属性(这里是m_age)拷贝给另一个对象。

class Person
{
public:
	int* m_age; //m_age存放的是年龄数据的地址,而不是数据本身
public:
	Person(int age)
	{
		m_age = new int(age);
	}
	~Person()
	{
		if (m_age != NULL)
		{
			delete m_age;
			m_age = NULL;
		}
	}
	
	//重载赋值=运算符
	Person& operator=(Person& p)
	{
		//应该先判断是否有属性在堆区,如果有,先释放干净,再深拷贝
		if (m_age != NULL)
		{
			delete m_age;
			m_age = NULL;
		}
		//编译器提供的是浅拷贝
		//m_age = p.m_age;
		m_age = new int(*p.m_age); //深拷贝操作
		return *this; //返回对象自身
	}
};


void test01()
{
	Person p1(18);
	Person p2(20);
	Person p3(30);

	p3 = p2 = p1; //赋值操作
	cout << "p1的年龄为: " << *p1.m_age << endl;
	cout << "p2的年龄为: " << *p2.m_age << endl;
	cout << "p3的年龄为: " << *p3.m_age << endl;


}

五、关系 运算符重载

可以让两个自定义类型对象进行对比操作,下面用 ==和!= 号举例。

class Person
{
public:
	string m_name;
	int m_age;
public:
	Person(string name, int age)
	{
		this->m_name = name;
		this->m_age = age;
	}

	//重载==号
	bool operator==(Person& p)
	{
		if (this->m_name == p.m_name && this->m_age == p.m_age)
		{
			return true;
		}
		return false;
	}

	//重载!=号
	bool operator!=(Person& p)
	{
		if (this->m_name == p.m_name && this->m_age == p.m_age)
		{
			return false;
		}
		return true;
	}
};

六、函数调用 运算符重载

  • 函数调用运算符()也可以重载
  • 由于重载后的使用方式非常像函数的调用,因此称为仿函数
  • 仿函数没有固定写法,非常灵活
//打印输出类
class MyPrint
{
public:
	//重载函数调用运算符
	void operator()(string test)
	{
		cout << test << endl;
	}
};

void MyPrint02(string test)
{
	cout << test << endl;
}

void test01()
{
	MyPrint myprint;
	myprint("hello world"); //由于使用起来非常类似于函数调用,因此称为仿函数
	MyPrint02("hello world");
}

test01可以看出,通过重载函数调用运算符实现的myprint也可以实现常规函数如MyPrint02的功能,因此这种函数调用运算符()重载也被称为仿函数。

下面再举例一个:

//仿函数非常灵活,没有固定写法
//加法类
class MyAdd
{
public:
	int operator()(int num1, int num2)
	{
		return num1 + num2;
	}
};

void test02()
{
	MyAdd myadd;
	int my_result = myadd(1, 2);
	cout << "my_result = " << my_result << endl;

	//匿名函数对象
	cout << MyAdd()(100, 100) << endl;
}

test02可以看出,对象myadd可以实现加法的功能。同时,由最后一行也可以看出直接使用MyAdd()创建匿名对象,然后再调用函数。

七、总结

  1. 下列操作符只能通过成员函数进行重载:= [] () ->
  2. <<>> 只能通过全局函数配合友元进行重载。
  3. 不能重载 &&|| ,因为无法实现短路原则(逻辑运算符是自左向右进行的,如果左边的结果已经能够决定结果了,就不会做右边的计算)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值