运算符重载概念:对已有的运算符重新定义,赋予其另一种功能,以适应不同的数据类型。
一、加号运算符重载
编译器本身掌握了1+1=2、 2+2=4之类的运算(内置数据类型),但当两个自定义的数据类型需要进行相加运算时,编译器不知道怎么处理。
我们当然可以通过函数来实现自定义数据类型的相加
class Person
{
public:
int m_A;
int m_B;
};
Person p1;
p1.m_A = 10;
p2.m_B = 10;
Person p2;
p2.m_A = 10;
p2.m_B = 10;
此时想实现两个person中A与B值的和,可以定义一个函数,其返回值为新的person:(其中this指针指向调用该函数的类中的成员变量)
Person PersonAddPerson(Person &p)
{
Person temp;
temp.m_A = this->m_A + p.m_A;
temp.m_B = this->m_B + p.m_B;
return temp;
}
对于相加函数的命名,每个人都各不相同。此时编译器为大家起了通用的名称,为:operator+ 函数的其他内容无需改变。
因此,当我们想使用此函数进行实际加法时:
Person p3 = p1.operator+(p2);
可以简化为
Person p3 = p1 + p2;
二、左移运算符重载
作用:可以输出自定义数据类型
int a = 10;
cout << a << endl;
Person p;
p.m_A = 10;
p.m_B = 10;
cout<< p << endl;
编译器可以对a进行输出,但是不知道p如何输出,需要重载左移运算符类型。通常采用全局函数重载左移运算符(成员函数无法实现cout在左侧)。
ostream & operator<< (ostream &cout , Person &p)
{
cout<<"m_A = "<<p.m_A <<"m_B = "<<p.m_B;
return cout;
}
三、递增运算符重载
作用:通过重载递增运算符,实现自己的整型数据
class MyInteger
{
public:
MyInteger()
{
m_Num = 0;
}
private:
int n_Num;
}
我们希望让MyInteger类实现整型数据一样的操作,能够递增。首先实现前置递增:
//前置递增
MyInteger& operator++()
{
m_Num++;
return *this;
}
void test01()
{
MyInteger myint;
cout << myint++ << endl;
}
1、首先,cout << myint++ << endl是我们最终的目标语句,不同于重载左移运算符,左移运算符与cout及之后要输出的内容关联,因此不仅需要形参,还需要用全局函数来定义;
2、递增运算符可以写在局部函数中,需要递增的量与重载函数都在同一个类中,可以直接调用,因此不需要形参;
3、在进行完递增操作后要返回自身,因此return *this;
4、返回值用引用的形式接收原因是:当重叠递增运算符时,如++(++myint),如果没有返回引用,第一次++后得到的是一个新的类,两次++后再输出myint结果只进行了一次,因此要引用。
其次实现后置递增:
MyInteger operator++(int)
{
MyInteger temp = *this;
m_Num++;
return temp;
}
1、后置递增与前置递增的区别是,后置递增会先返回原结果,再递增。因此我们要先返回输入时的结果,再进行递增操作。但又不能先return,这样函数会直接停止运行。因此,我们先定义一个类的实例来保存输入时的结果,在递增,再return。
2、此时不能用引用的形式来返回,因为引用作返回值时返回的不能是局部变量(局部变量保存在栈区,在函数结束后销毁),而返回值temp就是局部变量。因此这里直接返回值,不用引用。
四、赋值运算符重载
C++编译器至少给一个类添加4个函数:
1、默认构造函数
2、默认析构函数
3、默认拷贝构造函数
4、赋值运算符operator=,对属性进行值拷贝
对属性进行值拷贝与默认拷贝构造函数遇到的问题相似,也会出现深浅拷贝的问题,堆区内存重复释放。
解决方案:利用深拷贝解决浅拷贝带来的问题,因此要重写等号赋值运算。
//重载赋值运算符
Person& operator=(Person& p)
{
if (m_Age != NULL);
{
delete m_Age;
m_Age = NULL;
}
m_Age = new int(*p.m_Age);
return *this;
}
1、不同于之前学过的深浅拷贝,此时是进行赋值操作,在赋值前被赋值的类可能已经有值,因此要先判断属性是否为空,如果不为空先释放内存,再进行赋值。
2、等同于之前学过的深浅拷贝,将赋值的堆区内容解引用,重新开辟一块内存,在赋值给被赋值的对象。
3、想要实现连续赋值,返回值就必须还是自身,因此return *this。
五、关系运算符重载
作用:重载关系运算符,可以让两个自定义类型对象进行对比操作。
自定义的两个数据类型无法进行直接比较,需要重载关系运算符。
bool operator=(Person & p)
{
if(this->m_Name == p.m_Name && this-> m_Age == p.m_Age)
{
return true;
}
else
{ return false;
}
}
bool operator=(Person & p)
{
if(this->m_Name == p.m_Name && this-> m_Age == p.m_Age)
{
return false;
}
else
{ return true;
}
}
六、函数调用运算符重载
函数调用运算符()也可以重载,由于重载后使用的方式非常像函数的调用,因此称其为仿函数。它没有固定写法,非常灵活。
class MyPrint
{
public:
//重载函数调用运算符
void operator()(string test)
{
cout << test << endl;
}
};
void test01()
{
MyPrint myprint;
myprint("Hello World!");
}