运算符重载
持续更新,补充C++新增feature,目录结构遵循《C++ Primer》
- 普通的运算符只能用于基本数据类型
- 对抽象的数据类型也能使用C++提供的数据类型
- 代码更简洁
- 代码更容易理解
- 运算符重载的实质是函数重载,形式为:
返回值类型 operator 运算符(形参表){}
- 在程序编译时:
- 把运算符的表达式 -> 对运算符函数的调用
- 把运算符的操作数 -> 运算符函数的参数
- 运算符多次被重载时,根据实参类型决定调用哪个运算符函数
- 运算符可以被重载成普通函数
- 参数个数为运算符的目数(如
+
为二元运算符,因此参数个数为2)
class Complex { public: Complex(double r = 0.0, double i = 0.0) { real = r; image = i; } double real; double image; }; //普通的全局函数 Complex operator+ (const Complex& a, const Complex& b){ return Complex(a.real+b.real, a.image+b.image); }
- 参数个数为运算符的目数(如
- 也可以被重载成类的成员函数
- 参数个数为运算符目数减一
class Complex { public: Complex(double r = 0.0, double i = 0.0) { real = r; image = i; } Complex operator+ (const Complex& ); Complex operator- (const Complex& ); private: double real; double image; }; Complex Complex::operator+(const Complex& op) { return Complex(real+op.real,image+op.image); } int main() { Complex x, y(4.3,2.6), z(3.3,1.1); x = y+z; //=> x = y.operator+(z) return 0; }
- 重载
<<
C++中的cout<<
使用的也是运算符重载,cout
是ostream
类的对象,ostream
重载了<<
:
ostream& ostream::operator<<(int n){
return *this;
}
//cout<<3<<"this";`等价于:
//cout.operator<<(3).operator<<("this");
也可以重载<<
进行自定义输出
class Person{
private:
string name;
friend ostream& operator<<(ostream& out, const Person& p);
}
ostream& operator<<(ostream& out, const Person& p){
out<<p.name;
return out;
}
int main(){
Person p;
cout<<p;
}
赋值运算符重载
- 赋值运算符两边类型可以不匹配
- 赋值运算符
=
只能重载为成员函数
class string{
private:
char* p;
public:
string():p(NULL){}
const char* c_str(){ return p; }
char* operator=(const char* s){
if(p){
delete[] p;
}
if(s){
int len = strlen(s);
p = new char[len+1];
strcpy(p,s);
}
else{
p = NULL;
}
return p;
}
};
int main(){
string x1 = "abc"; //通过重载运算符实现
}
- 深浅拷贝
- 发生在两个对象互相赋值的过程中
- 浅拷贝的问题:如果成员变量有指针对象,那么浅拷贝会导致被复制的对象和原对象的指针成员变量值相同,即他们指向同一块内存区域,当对象析构时,会有double free的风险
string& operator=(string& s){
if(s.c_str() == p){ //自己赋值给自己
return *this;
}
if(p){
delete[] p;
}
p = new char[strlen(s.c_str()+1)];
strcpy(p,s.c_str());
return *this;
}
int main(){
string x1="abc";
string x2;
x2 = x1; //通过重载运算符实现
}
- 返回值不能设计成void,会有
a=b=c
的情况- 等价于
a.operator=(b.operator=(c))
- 等价于
- 返回值要设计成引用类型
- 运算结果最终还是作用于自身,因此返回值用引用
运算符重载为友元函数
- 成员函数不能满足使用要求
- 普通函数,又不能访问类的私有成员
自加/自减运算符重载
- 自加
++
, 自减--
运算符有前置/后置之分 - 前置运算符为一元运算符重载, 返回左值
- 重载为成员函数
//重载为成员函数 T operator++(); T operator--();
- 重载为全局函数
cpp //重载为全局函数: T operator++(T); T operator--(T);
` ++obj, obj.operator++(), operator++(obj)
都调用上述函数
- 后置运算符作为二元运算符重载
- 多写一个参数,具体无意义,返回右值
- 重载为成员函数
T operator++(int); //多写一个参数告诉编译器是后置运算,初始化为0 T operator--(int);
- 重载为全局函数
T operator++(T, int); T operator--(T, int); //第二个参数没有特殊意义, 默认初始化为0
obj++, obj.operator++(0), operator++(obj, 0)
都调用上述函数
CDemo
例子
class CDemo {
private :
int n;
public:
CDemo(int i=0):n(i) { }
CDemo & operator++(); //用于前置++形式
CDemo operator++(int); //用于后置++形式
operator int ( ) {
return n;
}
friend CDemo & operator--(CDemo &); //友元全局函数,用于前置--形式
friend CDemo operator--(CDemo &, int); //友元全局函数,用于后置--形式
};
CDemo & CDemo::operator++() { //前置 ++
n++;
return * this;
}
CDemo CDemo::operator++(int k) { //后置 ++
CDemo tmp(*this); //记录修改前的对象
n++;
return tmp; //返回修改前的对象
}
CDemo & operator--(CDemo & d) { //前置--
d.n--;
return d;
}
CDemo operator--(CDemo & d, int) { //后置--
CDemo tmp(d);
d.n -;
return tmp;
}
int main(){
CDemo d(5);
cout << (d++) << ","; //等价于 d.operator++(0);
cout << d << ",";
cout << (++d) << ","; //等价于 d.operator++();
cout << d << endl;
cout << (d--) << ","; //等价于 operator--(d,0);
cout << d << ",";
cout << (--d) << ","; //等价于 operator--(d);
cout << d << endl;
return 0;
}
类型强制转换运算符重载
- 有时需要对某个对象进行强制类型转换,转换规则也可以重载,例如:
operator int(){return n;}
int
作为一个类型强制转换运算符被重载,Demo s; (int)s
,等价于s.int()
- 类型强制装换运算符重载时
- 不能写返回值类型
- 实际上其返回值类型为强制转换后的类型
class Number
{
public:
int num;
Number(int n = 0) : num(n) {}
int operator*(const Number &n){
return num * n.num;
}
operator int(){
return num;
}
};
int main()
{
Number n1(10), n2(20);
Number n3;
n3 = n1 * n2;
cout << int(n3) << endl; //类型转换
return 0;
}
函数对象(functor)
- 定义:若一个类重载运算符
()
,则该类对象就称为函数对象 - 头文件:
<functional>
class A{
public:
double operator()(int a1, int a2, int a3){
return (double)(a1+a2+a3)/3;
}
};
A average; //函数对象
cout<<average(2,3,4); //averate.operator()(3,2,4)
- STL中的函数对象模板
equal_to
greater
less
greater
函数对象模板:
template<class T>
struct greater:public binary_function<T,T,bool>{
bool operator()(const T& x, const T& y)const{
return x>y;
}
}
运算符重载的注意事项
- C++不允许定义新的运算符
- 以下运算符不能被重载
.
,.*
,::
,?:
,sizeof
- 重载运算符
()
,[ ]
,->
或者赋值运算符=
时, 重载函数必须声明为类的成员函数