C++ 允许在同一作用域中的某个函数和运算符指定多个定义,分别称为函数重载和运算符重载。重载声明是指一个与之前已经在该作用域内声明过的函数或方法具有相同名称的声明,但是它们的参数列表和定义(实现)不相同。
当您调用一个重载函数或重载运算符时,编译器通过把您所使用的参数类型与定义中的参数类型进行比较,决定选用最合适的定义。选择最合适的重载函数或重载运算符的过程,称为重载决策。
您可以重定义或重载大部分 C++ 内置的运算符。这样,您就能使用自定义类型的运算符。
重载的运算符是带有特殊名称的函数,函数名是由关键字 operator 和其后要重载的运算符符号构成的。与其他函数一样,重载运算符有一个返回类型和一个参数列表。
运算符重载概念:对已有运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型
加号运算符重载
作用:实现两个自定义数据类型相加的运算
对于内置数据类型,编译器知道如何进行运算:10+10=20
两种实现方法:
- 成员函数重载运算符
- 全局函数重载运算符
本质:
- 成员函数:
A c = a.operator+(b)
- 全局函数:
A c = operator+(a, b)
class A
{
public:
A operator+ (A a)//必须要有加号+
{
A temp;
temp.m_a = this->m_a + a.m_a;
temp.m_b = this->m_b + a.m_b;
return temp;
}
int m_a = 10;
int m_b = 10;
};
int main()
{
A a;
A b;
//A c = a.operator+(b);
A c = a + b;
cout << c.m_a << endl << c.m_b << endl;
return 0;
}
class A
{
public:
int m_a = 10;
int m_b = 10;
};
A operator+(A a, A b)
{
A temp;
temp.m_a = a.m_a + b.m_a;
temp.m_b = a.m_b + b.m_b;
return temp;
}
int main()
{
A a;
A b;
//全局重载函数本质调用
//A c = operator+(a, b);
A c = a + b;
cout << c.m_a << endl << c.m_b << endl;
return 0;
}
运算符重载,也可以发生函数重载:
class A
{
public:
int m_a = 10;
int m_b = 10;
};
A operator+(A a, A b)
{
A temp;
temp.m_a = a.m_a + b.m_a;
temp.m_b = a.m_b + b.m_b;
return temp;
}
A operator+(A a, int b)
{
A temp;
temp.m_a = a.m_a + b;
temp.m_b = a.m_b + b;
return temp;
}
int main()
{
A a;
A b = a + 10;
cout << b.m_a << endl << b.m_b << endl;
return 0;
}
总结:
- 对于内置的数据类型的表达式的运算是不可能修改的
- 不要滥用运算符重载:加法写成减法,乘法写成除法
左移运算符重载
- 不会利用成员函数重载
<<
运算符,因为无法实现cout
在左侧 - 只能利用全局函数重载
<<
运算符
#include <iostream>
using namespace std;
class Person
{
//添加友元,以访问私有属性
friend ostream& operator<<(ostream& cout, Person& person);
private:
string name = "张三";
string phone = "110";
};
//cout的数据类型为ostream,返回值类型必须填ostream才能实现链式编程
//使用引用的方式,保证cout只有一个
ostream& operator<<(ostream& cout, Person& person)
{
cout << person.name << person.phone;
return cout;
}
int main()
{
Person a;
cout << a << endl;//链式编程
return 0;
}
递增运算符重载
通过重载递增运算符,实现自己的整型数据
注意事项:
- 前置递增要返回引用
- 后置递增要返回值,要用
int
占位参数,让编译器知道是在做后置递增
class Person
{
public:
//使用引用,为了一直对一个数据进行递增操作
Person& operator++()//重载前置++
{
age += 1;
return *this;
}
//不能用引用,temp局部变量会在运行结束后被销毁,非法访问
Person operator++(int)//int代表占位参数,可以用于区分前置和后置递增
{
Person temp = *this;//先记录当时的结果
age++;//后递增
return temp;//最后将记录结果做返回
}
int age;
};
ostream& operator<<(ostream& cout, Person a)
{
cout << a.age;
return cout;
}
int main()
{
Person a;
a.age = 10;
Person b;
b.age = 10;
cout << ++(++a) << endl;//链式编程
cout << a.age << endl;//检验是否成功递增
cout << b++ << endl;//链式编程
cout << b.age << endl;//检验是否成功递增
return 0;
}
赋值运算符重载
C++编译器会给一个类至少添加4个函数:
- 默认构造函数(无参,函数体为空)
- 默认析构函数(无参,函数体为空)
- 默认拷贝构造函数,对属性进行值拷贝
- 赋值运算符
operator=
,对属性进行值拷贝
如果类中有属性指向堆区,做赋值操作时也会出现深浅拷贝的问题
#include <iostream>
using namespace std;
class Person
{
public:
//重载赋值运算符
Person& operator=(Person a)
{
//编译器默认的浅拷贝
//m_name = a.m_name;
//应该先判断是否有属性在堆区,如果有先释放干净,然后再深拷贝
if (m_name != NULL)
{
delete m_name;
m_name = NULL;
}
//深拷贝
m_name = new string(*a.m_name);
//返回自身,以实现连等操作
return *this;
}
string* m_name;
};
int main()
{
Person a;
a.m_name = new string("张三");
Person b;
b.m_name = new string("李四");
Person c;
c.m_name = new string("王五");
a = b = c;
cout << *a.m_name << *b.m_name << *c.m_name << endl;
return 0;
}
关系运算符重载
作用:重载关系运算符,可以让两个自定义类型对象进行对比操作
对比时需要注意指针:
- 若不加
*
,则对比的是指针的地址 - 若加
*
,对比的是指针指向的值
#include <iostream>
using namespace std;
class Person
{
public:
Person(string name, int age)
{
m_name = new string(name);
m_age = age;
}
//重载关系运算符 ==
bool operator==(Person& a)
{
//m_name是指针,需要加解引用操作符,否则对比的是 指针指向的地址 而不是对比 对应的值
if (*this->m_name == *a.m_name && this->m_age == a.m_age)
{
return true;
}
return false;
}
int m_age;
string* m_name;
};
int main()
{
Person a("张三", 10);
Person b("张三", 10);
cout << (a == b) << endl;
return 0;
}
函数调用运算符重载
函数调用运算符()
也可以重载
由于重载后使用的方法非常像函数的调用,因此成为仿函数
仿函数没有固定写法,非常灵活
允许匿名对象调用
class Person
{
public:
int operator()(int a, int b)
{
return a + b;
}
};
int main()
{
Person a;
cout << a(1, 2) << endl;
//匿名对象调用
cout << Person()(1, 2) << endl;
return 0;
}