运算符重载
1 概述
C++规定运算符重载必须针对类的对象,即重载时至少有一个参数是对象,如A、const A、A &等等。没有对象就。new
一个出来
C++用operator加运算符进行。对于普通运算符成员函数,this
隐含参数代表第一个操作数对象。运算符可分为:
不能重载:sizeof、.、.*、::、?:
只能重载为普通成员函数:=、->、()、[]
只能重载为普通函数:new、delete
其他运算符可以重载为类的普通成员函数和普通函数,但不能重载为类的静态成员函数
若运算符为左值运算符,则重载后运算符应尽量返回非只读引用类型。当运算符要求第一个参数为左值时,不能使用const
说明第一个参数(含this
)。此外,重载不改变运算符的优先级和结合性。
需要注意,并不是含有类的名称的东西都是对象,如:
A &operator ==(A *a,A b[]); //错误,这俩都是指针类型变量,不是对象
2 运算符参数
重载函数的参数个数与重载函数种类有关:
重载为普通函数:参数个数=运算符目数
重载为普通成员:参数个数=运算符目数-1(有一个this指针)
重载为静态成员:参数个数=运算符目数(没有this)
有些特殊的运算符不满足上述关系:->
重载为单目,前置++
、--
为单目,后置++
、--
为双目,函数()
可重载为任意目。
关于()
运算符的重载,不加改动地照搬课件上令人难绷的代码。
#include <string.h>
class SYMTAB;
struct SYMBOL {
char *name; int value; SYMBOL *next; friend SYMTAB;
private:
SYMBOL(char *s, int v, SYMBOL *n) { /*...*/ }; ~SYMBOL( ) { /*…*/ }
} *s;
class SYMTAB {
SYMBOL *head;
public:
SYMTAB( ) { head = 0; }; ~SYMTAB( ) { /*...*/ }
SYMBOL *operator( )(char *s, int v, int w) { /*…*/};
} tab;
void main(void) { s = tab(“a”, 1, 2); } //等价 s = tab.operator()(“a”, 1, 2)
这里对SYMBOL
类中的括号运算符重载为了三目运算符。
下面介绍一些特殊运算符。
2.1 自增自减
上面已经介绍了自增自减运算符的双目、单目两种情况。除此之外,还要声明一点:后置运算应返回右值,前置运算应返回左值。这样更方便对象的连续运算。
应注意,自增自减最好返回对象类型而非int
,这更便于计算:
A operator++(int){ //后置
return A(x++); //巧妙的返回
}
A &operator++(){ //前置,返回引用类型
x++;
return *this;
}
2.2 双目箭头运算符
上面已经介绍,原先箭头运算符是双目的,重载应使其变成单目(只有一个参数)。重载->
必须返回指针类型。
下面是课件源代码,清晰地解释了语句i = b->a
的含义,不再解释。
struct A { int a; A(int x) { a = x; } };
class B {
A x;
public:
A *operator ->( ) { return &x; }; //只有一个参数this, 为单目
B(int v):x(v) { } //构造x(5)
} b(5);
void main(void) {
int i = b->a;
//上面语句翻译为:
i = b.operator->( ) ->a; //i = b.x.a = 5
i = (*b.operator->( )).a;
}
可以看出,->
重载的最大作用是简便代码的编写。
2.3 赋值
事实上,编译器为定义的类构造了缺省的赋值重载(浅拷贝)。即
A &operator=(const A&);
而若类自定义了重载赋值运算符函数,则会优先调用自定义的函数。
对于浅拷贝复制,若数据成员为指针类型,则不复制指针所指存储单元的内容。这可能会导致内存的泄露。因此对于成员包含指针的类,应尽量构造深拷贝赋值运算符重载函数。
为防止这种内存的泄露,可采用的方法有:
-
深拷贝,即先将指针指向的内容释放,再进行赋值和分配;
-
移动赋值,对纯右值使用,浅拷贝后令其指向
nullptr
。我也不明白为什么这样可以防止内存泄漏。
代码过于繁琐,详见课本相关内容。
3 强制类型转换
对类进行的强制类型转换同样可以进行重载。
需要强调的是,合适地编写构造函数可以利用构造函数直接进行类型转换。
class COMPLEX{
double r,v;
public:
COMPLEX(double r1);{/*...*/}
COMPLEX(double r1,double v1);{/*...*/}
COMPLEX operator+(const COMPLEX &c)const;
}
COMPLEX m(3);
cout<<m+2;
通过这种构造方式,3
自动转化为double
型的3.0
,m+2
则转换为m+2.0
,然后构造匿名对象COMPLEX(2.0)
,最终转换为m+COMPLEX(2.0)
。
而若想更清晰地表示强制类型转换,可以采用强制类型转换函数的方式:
operator 类型表达式()
如:
operator int()const{return i+j;} //转换为int型
operator A()const{return A(i+j);} //转换为类A型
对于new
和delete
的重载,不再解释。