C++运算符重载
运算符重载概念:对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型
运算符重载,也可以发生函数重载
1. 加号运算符(+)重载
作用:实现两个自定义数据类型相加的运算
代码实现
#include <iostream>
using namespace std;
class Person {
public:
//1.成员函数重载+号,本质调用为Person p3 = p1.operator+(p2);
Person operator+(Person& p)
{
Person temp;
temp.m_A = this->m_A + p.m_A;
temp.m_B = this->m_A + p.m_B;
return temp;
}
int m_A;
int m_B;
};
//2.全局函数重载+号,本质调用为Person p3 = operator+(p1,p2);
//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;
//}
void test01()
{
Person p1;
p1.m_A = 10;
p1.m_B = 10;
Person p2;
p2.m_A = 20;
p2.m_B = 20;
Person p3 = p1 + p2;
cout << "p3.m_A= " << p3.m_A << endl;
cout << "p3.m_B= " << p3.m_B << endl;
}
int main()
{
test01();
return 0;
}
左移运算符(cout)重载
作用:可以输出自定义的数据类型
代码实现
#include <iostream>
using namespace std;
class Person {
public:
//通常,我们不会利用成员函数重载左移运算符,因为无法将cout在左侧实现,调用时得写成p<<cout
int m_A;
int m_B;
};
//利用全局函数重载左移运算符
ostream& operator<<(ostream& cout, Person& p)
{
cout << "p.m_A= " << p.m_A << endl;
cout << "p.m_B= " << p.m_B << endl;
return cout;
}
int main()
{
Person p;
p.m_A = 10;
p.m_B = 10;
cout << p << endl;
return 0;
}
- 如果需要输出类中的私有内容,记得要将重载函数设置为友元
- 将重载函数cout输出类型设置为ostream,函数后可以继续追加其他输出值,如
cout <<p<<"hello world"<<endl;
递增运算符(++)重载
作用:通过重载递增运算符,实现自己的整形数据
代码实现
#include <iostream>
using namespace std;
class MyInteger
{
public:
MyInteger()
{
m_Num = 10;
}
//重载前置++运算符(++i)
MyInteger &operator++()
{
++m_Num;
return *this; //将类本身做返回
}
//重载后置++运算符(i++)
//因为函数返回的是局部变量temp,当函数调用完后会释放局部变量,所以函数只能返回值,而不是引用&
MyInteger operator++(int) //int 代表占位参数,用于区分前置和后置递增
{
MyInteger temp = *this;
m_Num++;
return temp; //将类本身做返回
}
int m_Num;
};
ostream &operator<<(ostream &cout, MyInteger &myint)
{
cout << myint.m_Num;
return cout;
}
int main()
{
MyInteger myint;
cout << ++myint << endl;
return 0;
}
递增运算符分前置递增(++i)和后置递增(i++),两者写法分别为:
-
重载前置递增运算符:
函数返回值 &operator++();
-
重载后置递增运算符:
函数返回值 operator++(int);
注意:
(1).重载前置运算符不需要传入参数,而重载后置运算符需要传入一个占位参数int;
(2).重载前置运算符返回的是引用,后续可以对该值继续链式调用;
(3).重载后置运算符直接返回局部变量的值,因为局部变量在函数调用完后就被释放,所以如果引用一个局部变量地址就会报错。
赋值运算符(=)重载
C++编译器至少给一个类添加了4个默认函数:
- 默认构造函数(无参)
- 默认析构函数(无参)
- 默认拷贝构造函数,对属性进行浅拷贝
- 赋值运算符operator=,对属性进行值拷贝
只要涉及到值的拷贝问题,就必须要搞清楚是深拷贝(拷贝对象和原对象指向不同存储空间)还是浅拷贝(拷贝对象和原对象指向同一片存储空间,可能引发释放问题)
代码实现
#include <iostream>
using namespace std;
class Person {
public:
int *m_Age;
Person(int age)
{
m_Age = new int(age);
}
~Person() //写出析构函数是为了方便看出浅拷贝和深拷贝的区别
{
if (m_Age) {
delete m_Age;
m_Age = NULL;
}
}
Person &operator=(Person& p)
{
//先判断是否有属性在堆区,如果有先释放干净,然后再深拷贝
if (m_Age)
{
delete m_Age;
m_Age = NULL;
}
//深拷贝
m_Age = new int(* p.m_Age);
//编译器只提供如下浅拷贝:
//m_Age = p.m_Age;
return *this;
}
};
void test()
{
Person p1(10);
Person p2(20);
p2 = p1; //赋值操作,调用重载函数
cout << "p1.m_Age= " << *p1.m_Age << endl;
cout << "p2.m_Age= " << *p2.m_Age << endl;
}
int main()
{
test();
return 0;
}
关系运算符(==)重载
在做对比操作时,如果需要对比的是两个类中的成员值,这就需要我们自己重载等值运算符,选取需要对比的成员。
代码实现
#include <iostream>
using namespace std;
class Person {
public:
string m_Name;
int m_Age;
Person(string name, int age) //有参构造函数
{
m_Name = name;
m_Age = age;
}
bool operator==(Person& p) //等值运算符重载
{
if (this->m_Name == p.m_Name && this->m_Age == p.m_Age) {
return true;
}
else {
return false;
}
}
};
int main()
{
Person p1("张三",10);
Person p2("李四", 20);
if (p1 == p2) {
cout << "p1 和 p2相等" << endl;
}
else {
cout << "p1 和 p2不相等" << endl;
}
return 0;
}
同样的道理,也可以重载!=
运算符、小于<
运算符、大于>
运算符等。
函数调用运算符()重载
没错,这里重载的就是一对小括号()
- 由于重载后使用的方式比较像函数的调用,使用也称之为仿函数。仿函数没有固定写法,非常灵活
代码实现
#include <iostream>
using namespace std;
class MyPrint
{
public:
void operator()(string str)
{
cout << str << endl;
}
};
int main()
{
MyPrint myprint;
myprint("hello world!"); //这里的()使用了重载函数,其本身也像一个函数
return 0;
}
上面代码中是将()
重载为了输出函数,作为仿函数,它可以实现各种功能,前面几种重载类型几乎都可以用调用运算符重载实现(没去试验,我猜的),注意后期使用不要混淆。