C++类和对象(六) 运算符重载
概念: 对已有的运算符进行重新定义,赋予其另一种功能, 以适应不同的数据结构
1. 加号运算符重载
作用:
实现两个自定义数据类型的相加的运算
编译器提供了一个名称: operator+
成员函数运算符重载
class Person {
public:
int m_a;
int m_b;
//成员函数运算符重载
Person operator+(Person& p)
{
Person tmp;
tmp.m_a = this->m_a + p.m_a;
tmp.m_b = this->m_b + p.m_b;
return tmp;
}
};
p3 = p1 + p2 // p3 = p1.operator+(p2);
全局函数运算符重载
Person operator+(Person& p1, Person& p2)
{
Person tmp;
tmp.m_a = p1.m_a + p2.m_a;
tmp.m_b = p1.m_b + p2.m_b;
return tmp;
}
p3 = p1 + p2 // p3.operator(p1, p2)
函数也可以发生重载
Person operator+(Person &p, int n)
{
Person tmp;
tmp.m_a = p.m_a + n;
tmp.m_b = m.m_b + n;
return tmp;
}
p1 = p + 4 // p1.operator(p , 4)
2. 左移运算符重载
作用:
输出自定义的数据类型
//成员函数实现不了 cout << p; (只能 p << cout 格式不对)
//ostream 对象只能有一个 所以用引用的方式返回
ostream& operator<<(ostream &cout, Person& p)
{
cout << "p.m_a = " << p.m_a << "\np.m_b = " << p.m_b;
return cout;//链式编程,不返回的话,后面再接 << 会报错
}
3. 递增运算符重载
作用:
通过重载递增运算符,实现自己的整型数据
#include <iostream>
using namespace std;
class Person {
friend ostream& operator<<(ostream& cout, Person p);
public:
Person(int a) :m_a(a) {};
//前置++, 返回引用是为了一直对一个数据进行操作
Person& operator++()
{
m_a++;
return *this;
}
//后置++, int表示占位符, 区分前置后置++
//如果这里返回值为引用,因为tmp为局部变量,用完会释放掉,使用引用就是非法操作
Person operator++(int)
{
Person tmp = *this;
m_a++;
return tmp;
}
private:
int m_a;
};
ostream& operator<<(ostream &cout, Person p)
{
cout << p.m_a;
return cout;
}
void func()
{
Person p1(10);
cout << ++p1 << endl;
//cout << p1++ << endl;
cout << p1 << endl;
}
int main()
{
func();
system("pause");
return 0;
}
4. 赋值运算符重载
C++编译器至少给一个类添加4个函数
- 默认构造函数(无参,函数体为空)
- 默认析构函数(无参,函数体为空)
- 默认拷贝构造函数, 对属性进行值拷贝
- 赋值运算符 operator=, 对属性进行值拷贝
如果类中有属性指向堆区, 做赋值操作也会出现深浅拷贝问题
深浅拷贝的图示:
普通的赋值运算符的错误示范:
Person p1(10);
Person p2(9);
cout << p1.m_a << endl;
cout << p2.m_a << endl;
p2 = p1;//编译器提供的简单的值拷贝的赋值运算符
cout << p1.m_a << endl;
cout << p2.m_a << endl;
运行显示,开始两个对象的成员的地址是不一样的
使用编译器提供的赋值运算符后,地址变为了一样
说明两个对象的成员又指到了同一个位置
再次析构的时候必然会发生错误
正确的改法:
//赋值操作符重载
Person& operator=(const Person& p)
{
//m_a = p.m_a;//编译器提供的简单的浅拷贝
//先判断是否有属性再堆区如果有,先释放干净
if(m_a != NULL)
{
delete m_a;
m_a = NULL;
}
//深拷贝
m_a = new int(*p.m_a);//重新再堆区开辟了内存空间
return *this;//返回自身
}
加引用才返回真正的自身,不然返回的是自身的拷贝
return *this 返回自身
5. 关系运算符重载
作用:
重载关系运算符,可以让两个自定义类型的对象进行对比操作
bool operator==(Person& p)
{
if (m_a == p.m_a)
return true;
else
return false;
}
bool operator!=(Person& p)
{
if (m_a == p.m_a)
return false;
else
return true;
}
6. 函数调用运算符重载
函数调用运算符 () 也可以重载
由于重载后的使用方式非常像函数调用,也叫仿函数(重载小括号)
仿函数没有固定的写法,非常灵活
class MyPrint {
public:
void operator()(string str)
{
cout << str << endl;
}
};
class MyAdd {
public:
int operator()(int a, int b)
{
return a + b;
}
};
void func()
{
MyPrint mp;
mp("hello");
MyPrint()("world");//匿名函数对象调用
MyAdd myadd;
cout << myadd(3, 4) << endl;
}
MyPrint()在调用一个匿名对象,然后再调用函数
匿名对象当前行调用,当前行执行完释放
以后看到一个类型加小括号第一反应 这就是匿名对象,后面小括号就是使用它重载的运算符