C++学习笔记(十四)

一、运算符重载

运算符重载概念:对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型

1.1 加号运算符重载

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

cpp 复制代码
#include <iostream>

using namespace std;

class Person
{
public:
	// 1. 成员函数重载+号
	/*Person operator+(Person& p)
	{
		Person temp;
		temp.m_a = this->m_a + p.m_a;
		temp.m_b = this->m_b + p.m_b;
		return temp;
	}*/
	int m_a;
	int m_b;
};

 // 2. 全局函数重载+号
Person operator+(Person& p1, Person& p2)
{
	Person temp;
	temp.m_a = p1.m_a + p2.m_a;
	temp.m_b = p1.m_b + p2.m_b;
	return temp;
}

// 运算符重载也可以发生函数重载
Person operator+(Person& p1, int num)
{
	Person  temp;
	temp.m_a = p1.m_a + num;
	temp.m_b = p1.m_b + num;
	return temp;
}

void test()
{
	Person p1;
	p1.m_a = 10;
	p1.m_b = 20;

	Person p2;
	p2.m_a = 5;
	p2.m_b = 1;

	// 成员函数重载
	Person p3;
	// p3 = p1.operater+(p2);
	p3 = p1 + p2;
	cout << "p3.m_a = " << p3.m_a << endl;
	cout << "p3.m_b = " << p3.m_b << endl;

	// 全局函数重载
	Person p4;
	// p3 = operator+(p1,p2);
	p4 = p1 + p2;
	cout << "p4.m_a = " << p4.m_a << endl;
	cout << "p4.m_b = " << p4.m_b << endl;

	// 运算符重载也可以发生函数重载
	Person p5;
	p5 = p1 + 100;
	cout << "p5.m_a = " << p5.m_a << endl;
	cout << "p5.m_b = " << p5.m_b << endl;
}

int main(int argc, char* argv[])
{
	test();
	return 0;
}

总结1:对于内置的数据类型的表达式的运算符是不可能改变的

总结2:不要滥用运算符重载

1.2 左移运算符重载

作用:可以输出自定义数据类型

cpp 复制代码
#include <iostream>

using namespace std;

class Person
{
	friend ostream& operator<<(ostream& cout, Person& p);
	// 利用成员函数重载左移运算符 p.operator<<(p1) === p << p1
	// p.operator<<(cout) === p << cout
	// 综上所述一般不会利用成员函数重载<<运算符,因为无法实现cout在左侧
public:
	void setA(int a)
	{
		m_a = a;
	}
	void setB(int b)
	{
		m_b = b;
	}
private:
	int m_a;
	int m_b;
};

ostream& operator<<(ostream& cout, Person& p)
{
	cout << "m_a = " << p.m_a << endl;
	cout << "m_b = " << p.m_b;
	return cout;
}

int main(int argc,char* argv[])
{
	Person p;
	p.setA(10);
	p.setB(13);
	cout << p << endl;
	return 0;
}

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

1.3 递增运算符重载

作用:通过重载递增运算符,实现自己的整型数据

cpp 复制代码
#include <iostream>

using namespace std;

class MyInteger
{
	friend ostream& operator<<(ostream& cout, MyInteger myint);
public:
	MyInteger()
	{
		m_num = 0;
	}

	// 重置前置++运算符 返回应用是为了一直对一个数据进行递增操作
	MyInteger& operator++()
	{
		// 先++
		m_num++;
		// 再返回
		return *this;
	}

	// 重置后置++运算符 int代表占位参数,可以用于区分前置和后置递增
	MyInteger operator++(int)
	{
		// 先返回数据,再递增
		// 如果先return返回后续递增不执行
		// 转变成先记录数据,再递增,最后返回记录的数据
		MyInteger temp = *this;
		m_num++;
		return temp;
	}

private:
	int m_num;
};

// 重载 << 运算符
ostream& operator<<(ostream& cout, MyInteger myint)
{
	cout << myint.m_num;
	return cout;
}

void test01()
{
	MyInteger myint;
	cout << ++(++myint) << endl;
	cout << myint << endl;
}

void test02()
{
	MyInteger myint;
	cout << (myint++)++ << endl;
	cout << myint << endl;
}

int main(int argc, char* argv[])
{
	test01();
	test02();
	return 0;
}

总结:前置递增返回的是引用,后置递增返回的是值

1.4 赋值运算符重载

C++编译器至少给一个类添加4个函数

  1. 默认构造函数(无参,函数体为空)

  2. 默认析构函数(无参,函数体为空)

  3. 默认拷贝构造函数,对属性进行值拷贝

  4. 赋值运算符 operator=,对属性进行值拷贝

如果类中有属性指向堆区,做赋值操作时也会出现深浅拷贝问题

cpp 复制代码
#include <iostream>

using namespace std;

class Person
{
public:
	Person(int age)
	{
		m_age = new int(age);
	}
	int* m_age;
	~Person()
	{
		if (m_age != NULL)
		{
			delete m_age;
			m_age = NULL;
		}
	}
	// 利用深拷贝解决浅拷贝带来的问题
	Person& operator=(Person& p)
	{
		// 编译器提供的是浅拷贝
		// m_age = p.m_age;

		// 应该先判断是否有属性在堆区,如果有先释放干净,然后再深拷贝
		if (m_age != NULL)
		{
			delete m_age;
			m_age = NULL;
		}
		// 深拷贝
		m_age = new int(*p.m_age);

		return *this;
	}
};

void test01()
{
	Person p1(18);
	cout << "p1的年龄为:" << *p1.m_age << endl;
	Person p2(23);
	cout << "p2的年龄为:" << *p2.m_age << endl;
	Person p3(30);
	cout << "p2的年龄为:" << *p2.m_age << endl;
	// 浅拷贝会导致堆区内存重复释放
	// 赋值运算符重载
	p3 = p2 = p1;
	cout << "p2的年龄为:" << *p2.m_age << endl;
	cout << "p2的年龄为:" << *p2.m_age << endl;
	cout << "p2的年龄为:" << *p2.m_age << endl;
}

int main(int argc, char* argv[])
{
	test01();
	return 0;
}

1.5 关系运算符重载

作用:重载关系运算符,可以让两个自定义类型对象进行对比操作

cpp 复制代码
#include <iostream>
#include <string>

using namespace std;

class Person
{
public:
	Person(string name, int age)
	{
		m_Name = name;
		m_Age = age;
	}
	bool operator==(Person& p)
	{
		if (this->m_Age = p.m_Age && this->m_Name == p.m_Name)
		{
			return true;
		}
		return false;
	}
	string m_Name;
	int m_Age;
};

void test01()
{
	Person p1("张三", 18);
	Person p2("李四", 23);
	Person p3("李四", 23);
	if (p1 == p2)
	{
		cout << "p1等于p2" << endl;
	}
	else
	{
		cout << "p1不等于p2" << endl;
	}
	if (p2 == p3)
	{
		cout << "p2等于p3" << endl;
	}
	else
	{
		cout << "p2不等于p3" << endl;
	}
}

int main(int argc, char* argv[])
{
	test01();
	return 0;
}

1.6 函数调用运算符重载

函数调用运算符 () 也可以重载

由于重载后使用的方式非常像函数的调用,因此称为仿函数

仿函数没有固定写法,非常灵活

cpp 复制代码
#include <iostream>
#include <string>

using namespace std;

class MyPrint01
{
public:
	// 重载函数调用运算符
	void operator()(string text)
	{
		cout << text << endl;
	}
};

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

class MyAdd01
{
public:
	// 重载函数调用运算符
	double operator()(double a, double b)
	{
		return a + b;
	}
};

double MyAdd02(double a, double b)
{
	return a + b;
}

void test01()
{
	MyPrint01 MyPrint01;
	MyPrint01("hello world");
	MyPrint02("hello world");

	MyAdd01 myAdd01;
	double result01 = myAdd01(10, 20);
	double result02 = MyAdd02(10, 20);
	cout << "myAdd01仿函数计算的结果为:" << result01 << endl;
	cout << "MyAdd02函数计算的结果为:" << result02 << endl;

	// 匿名函数对象 类名+()为匿名对象,后面的重载的运算符
	cout << "匿名函数对象的计算结果为:" << MyAdd01()(10, 20) << endl;
}

int main(int argc, char* argv[])
{
	test01();
	return 0;
}
相关推荐
神经网络的应用30 分钟前
C++程序设计例题——第三章程序控制结构
c++·学习·算法
南宫生42 分钟前
力扣-数据结构-3【算法学习day.74】
java·数据结构·学习·算法·leetcode
挥剑决浮云 -1 小时前
STM32学习之 按键/光敏电阻 控制 LED/蜂鸣器
c语言·经验分享·stm32·单片机·嵌入式硬件·学习
工业甲酰苯胺1 小时前
聊一聊 C#线程池 的线程动态注入
java·开发语言·c#
zfenggo1 小时前
c/c++ 无法跳转定义
c语言·开发语言·c++
图灵猿1 小时前
【Lua之·Lua与C/C++交互·Lua CAPI访问栈操作】
c语言·c++·lua
向宇it1 小时前
【从零开始入门unity游戏开发之——C#篇30】C#常用泛型数据结构类——list<T>列表、`List<T>` 和数组 (`T[]`) 的选择
java·开发语言·数据结构·unity·c#·游戏引擎·list
keep-learner1 小时前
Unity Dots理论学习-2.ECS有关的模块(1)
学习·unity·游戏引擎
hakesashou1 小时前
python怎么看矩阵维数
开发语言·python
daopuyun1 小时前
GB/T34944-2017 《Java语言源代码漏洞测试规范》解读——安全功能
java·开发语言·安全