介绍C++的运算符重载及其原理,梳理一下注意要点,融入自己的理解,用一个例子贯穿,学浅勿喷。
使用一个例子引入运算符重载。
首先,定义一个复数类Complex
:
class Complex{
private:
int real;
int virt;
public:
Complex(int r, int v){real=r;virt=v;}
}
现在假设我们要实现两个复数相加,没有运算符重载之前,可以增加一个成员函数add:
Complex Complex::add(const Complex & c) const {
return Complex(real+c.real, virt+c.virt);
}
可以调用add函数实现复数相加:c3 = c1.add(c2)
,但是如果能使用c3 = c1 + c2
的表达式形式,显然更便捷易懂。这里就需要运算符重载,重载运算符之后,就能直接使用运算表达式对自定义类型进行运算。
运算符重载通过运算符函数实现,这是一类特别的函数,函数名必须是operator
关键字后跟要重载的运算符,参数列表与运算符的操作数相关(如何相关在后面说),其它的与普通函数一样。上面的add()函数,我们只需要修改函数名为operator+
就成功重载了+
运算:
Complex Complex::operator+(const Complex & c) const {
return Complex(real+c.real, virt+c.virt);
}
现在,就可以使用c3=c1+c2
来进行两个复数相加了。另外也可以使用函数调用c3=c1.operator+(c2)
。
总结就是重载运算符可以支持自定义类型进行运算符运算,为编程提供便利。
可以看到,运算符重载本质上是函数重载,函数重载就是对同一个函数名对不同的参数列表有不同的功能,运算符重载也是使一个运算符对不同的操作数有不同的运算逻辑。更进一步,上面c1+c2
与c1.operator+(c2)
是等效的,可以猜想,重载的运算符的最终是通过对应的运算符函数实现的。
那么,运算符与运算符函数具体是如何转化的?首先,运算符匹配很简单,运算符匹配直接找对应operator后面的运算符同样的函数即可。关键在操作数和参数列表的匹配,参数的匹匹配其实也很简单,就是单纯的按运算数从左到右作为参数代入而已(成员函数第一个参数是this)。比如,c1+c2
与c1.operator+(c2)
等效,与c2.operator+(c1)
不等效,即使计算结果一样。
重载一下*
运算符实现复数倍乘,形如c1 * 3
,增加一个如下成员函数:
Complex Complex::operator*(int n) const{
return Complex(n*real, n*virt);
}
即可使用c2=c1*3
实现复数倍乘。按运算习惯,有时候我们也会写成c2=3*c1
,但这其实是错误的表达,因为*
运算符并不存在参数是(int, Complex)的函数。所以必须再次重载运算符,注意这里第一个参数是int类型,成员函数不适用,只能用非成员函数。
//注意该函数应该为Complex的友函数,才能访问real,virl属性.
Complex operator*(int n,const Complex& c){
return Complex(n*c.real, n*c.virt);
}
这样,就可以使用类似c2=3*c1
的表达式了。
再次总结,运算符重载可以使用成员函数或非成员函数的方式,成员函数的第一个参数默认是所属类对象,如果运算符的第一个操作数是其它类型,必须使用非成员函数,且常常声明为友函数。
对运算符重载原理基本清楚之后,现在做下归纳,透彻掌握下。
首先,总结一下我们的主角们——运算符。并非所有的运算符都能重载,常见的能重载的运算符有:
+
, -
, *
, /
, %
。==
, <
, >
, <=
, >=
, !=
。&&
, ||
, !
。&
,|
, ^
,~
, >>
, <<
。=
, +=
, -=
, *=
, /=
,%=
, &=
, |=
, ^=
, <<=
, >>=
。+
(正), -
(负), &
(引用), *
(指针), ++
, --
, new
, new[]
, delete
, delete[]
。[]
(下标), ->
(成员访问), ()
(函数调用),,
(逗号)。不能重载的运算符有:.
, .*
, .->
, ?:
, ::
以及sizeof
类单词运算符。
运算符重载有一定的限制:
+
, 进行减操作,当然这点编译器无法判断。编写一些实例巩固一下,注意个别运算符重载的细节。
自增(自减)虽然是单目,但是有前置或后置两种用法,所以默认前置,使用int参数标记后置。用成员函数的方式重载前置++实现实部加+,后置++实现虚部加1,都返回改变后的值。
Complex& operator++(){ //前置++
++real;
return *this;
}
Complex& operator++(int){//后置++
++virt;
return *this;
}
这里返回引用的目的是支持连续调用。如果使用非成员函数的方式,需要增加第一个参数类型为Complex
,函数签名为:
Complex& operator++(Complex& c); //前置
Complex& operator++(Complex& c,int); //后置
输入输出<<
, >>
本来就是由位运算重载过来的。为了支持我们输入与输出Complex类型数据,需要为它重载。因为第一个对象是std::cin
和std::cout
,所以只能用非成员函数的方式实现。
istream& operator>>(istream& input, Complex& c){ //重载>>
int r,v;
char ch;
input>>c.real;
input>>ch;//过滤+
input>>c.virt;
input>>ch;//过滤j
return input;
}
ostream& operator<<(ostream& output, const Complex& c){//重载<<
output<
这里返回引用的目的也是支持连续调用,如cin>>c1>>c2;
。重载之后,就可以实现输入输出,如:
Complex c1;
std::cin>>c1; //输入形如1+2j
std::cout<
()
运算符其实是一个多元运算符,允许重载进行多个操作数的运算。 下面重载一个三个操作数(包括this)的。
Complex& Complex::operator()(int a,int b){
real+=a;
virt+=b;
return *this;
}
重载之后,便可以使用c1(3, 4);
实现实部加3虚部加4的运算。可以发现,()
像是一个与对象同名的函数。
各个运算符的重载就不详举了…原理都差不多。想看更多例子,可以看菜鸟教程。完。