一、运算符重载
在C++中会把运算符当做函数来处理 ,当我们使用运算符时,编译器其实是把运算符翻译成函数,由于C++支持函数重载,所以C++中的运算符也是可以重载的,这样可以对类对象进行定制操作,从而简化操作、提高代码的可读性,如:string类就是一个支持众多运算符的字符串类。
A a,b;
当对象支持+运算符时:a+b;
成员函数:
const A& operator+(cosnt A& b);
全局函数:
const A& operator+(const A& a,const B& b);
二、双目运算符重载
双目运算符重载的运算结果是一个临时对象,所以返回值应该加const。
成员函数:
const A operator#(const A& b)const
{
return 类(a参数#b参数);
}
全局函数:
const A operator#(const A& b,const B& b)
{
return 类(a参数#b参数);
}
#include <iostream>
using namespace std;
class Point
{
int x;
int y;
public:
Point(int x,int y):x(x),y(y) {}
void show(void)const
{
cout << "(" << x << "," << y << ")" << endl;
}
/*
const Point operator+(const Point& that)const
{
return Point(x+that.x,y+that.y);
}
*/
friend const Point operator+(const Point& a,const Point& b);
};
const Point operator+(const Point& a,const Point& b)
{
return Point(a.x+b.x,a.y+b.y);
}
int main(int argc,const char* argv[])
{
Point p(1,1);
Point p1(2,3);
p.show();
(p+p1).show();
}
注意:全局运算符函数中可能会访问到类的私有、保护的成员变量,C++提供的解决方法是把全局运算符函数声明为类的友元函数,那么在友元函数中就可以访问到类的私有、保护类的成员。
友元
在类的外部某个函数中想访问类的私有、保护类的成员函数,需要把函数声明为类的友元函数,但友元仅仅是朋友,它只有访问权没有拥有权(没有this指针)
友元声明
把函数的声明写到类中一份,并在函数的声明前面加上friend关键字,使用引用友元可以确保类的封装性。
注意:友元函数不会与成员函数构成重载,因为它们不在一个作用域内,但依然会冲突
☆三、输入输出运算符重载
- cout是ostream类型对象,cin是istream类型对象。
- 当我们想让一种类型方便地使用cin/cout运算符时,就需要把它们重载。
- 根据双目运算符的解析规则,如果重载为成员函数,则函数调用者为cin/cout,但我们无法到istream/ostream类中修改代码,因此从只能重载为全局函数。
ostream& operator<<(ostream& os,const A a)
istream& operator>>(istream& is,A& a)
#include <iostream>
using namespace std;
class Point
{
int x;
int y;
public:
Point(int x,int y):x(x),y(y) {}
void show(void)const
{
cout << "(" << x << "," << y << ")" << endl;
}
const Point operator+(const Point& that)const
{
return Point(x+that.x,y+that.y);
}
friend ostream& operator<<(ostream& os,const Point p);
friend istream& operator>>(istream& is,Point& p);
};
ostream& operator<<(ostream& os,const Point p)
{
return os << "(" << p.x << "," << p.y << ")";
}
istream& operator>>(istream& is,Point& p)
{
cout << "输入x的值:";
is >> p.x ;
cout << "输入y的值:";
return is >> p.y;
}
int main(int argc,const char* argv[])
{
Point p(1,1);
Point p1(2,3);
p.show();
(p+p1).show();
cout << "-----------------" << endl;
cout << p << p1 << endl;
cout << "-----------------" << endl;
cin >> p;
p.show();
}
注意:在输入输出过程中,cin/cout会记录错误、状态标志,所以不能加const
四、赋值类型运算符重载
成员函数:
void operator=(const A& b)
{
return 类(a参数#b参数);
}
注意赋值运算符的调用
A a = val; //调用单参构造
a = b; //调用赋值运算符
注意:一般不需要重写赋值运算符,类中有缺省的赋值运算符,当需要深拷贝时才需要显示实现。
五、单目运算符重载
成员函数:
[const] A operator#(void)[const]
{
}
全局函数:
[const] A operator#([const] A& that)
{
}
前–/++
成员函数:
A& operator++(void)
{
x++,y++;
return *this;
}
全局函数:
A& operator--(A& that)
{
p.x--,p.y--;
return p;
}
后–/++
成员函数:
A operator--(int)
{
return Point(x--,y--);
}
全局函数:
A operator++(A& that,int)
{
return Point(p.x++,p.y++);
}
注意:前自变和后自变的参数基本一样,唯一的区别是哑元
六、特殊运算符的重载
[] //下标运算符,重载了它类对象就可以伪装成数组使用,STL模板库中大多数容器都重载了它。
() //函数操作运算符,重载了它类对象可以伪装成函数使用。
* //解引用运算符,
-> //成员访问运算符,重载了它对象可以伪装成指针使用,STL中的智能指针就是通过重载它们实现的。
new/delete //堆内存管理运算符
1、C++默认的堆内存管理器速度比较慢,重载new/delete底层就可以使用malloc/free提高运行速度。
2、当申请内存比较小时,可能会产生内存碎片,重载new之后可以扩大小字节块的内存,减小内存碎片产生的几率。
3、重载new/delete可以在重载函数中记录内存的使用信息,帮助解决内存泄露的问题。
void* operator new(size_t len)
{
void* ptr = malloc(len);
cout << "new:" << ptr << endl;
return ptr;
}
void operator delete(void* ptr)
{
free(ptr);
cout << "delete:" << ptr << endl;
}
七、重载操作符的限制
1、只能重载为成员函数的运算符
= [] () -> *
2、只能重载为全局函数的运算符
<< >>
3、不能重载的运算符
:: 域限定符
. 直接成员访问符
?: 三目运算符
sizeof 字符长度运算符
typeid 类型信息运算符
4、重载运算符无法改变运算符的优先级
5、无法重载基本运算符的运算符
6、不能改变运算符的参数个数
7、无法发明新的运算符
八、运算符重载的建议
1、重载运算符时,要根据运算符的实际功能和意义来确定具体的参数、返回值是否需要const属性,返回值是否引用或临时对象。
2、重载运算符要符合情理,要以实际用途为前提。
3、重载运算符是为了让对象的操作更简单、方便,提高代码的可读性,而不是为了炫技。
4、重载运算符时,要与运算符默认的功能以及运算规则一致,不要有反人类的操作。