运算符重载
介绍C++的运算符重载及其原理,梳理一下注意要点,融入自己的理解,用一个例子贯穿,学浅勿喷。
1、引入
使用一个例子引入运算符重载。
首先,定义一个复数类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)。
总结就是重载运算符可以支持自定义类型进行运算符运算,为编程提供便利。
2、深入
可以看到,运算符重载本质上是函数重载,函数重载就是对同一个函数名对不同的参数列表有不同的功能,运算符重载也是使一个运算符对不同的操作数有不同的运算逻辑。更进一步,上面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的表达式了。
再次总结,运算符重载可以使用成员函数或非成员函数的方式,成员函数的第一个参数默认是所属类对象,如果运算符的第一个操作数是其它类型,必须使用非成员函数,且常常声明为友函数。
3、掌握
对运算符重载原理基本清楚之后,现在做下归纳,透彻掌握下。
3.1、运算符
首先,总结一下我们的主角们——运算符。并非所有的运算符都能重载,常见的能重载的运算符有:
- 运算算术符:
+,-,*,/,%。 - 关系运算符:
==,<,>,<=,>=,!=。 - 逻辑运算符:
&&,||,!。 - 位运算符:
&,|,^,~,>>,<<。 - 赋值运算符:
=,+=,-=,*=,/=,%=,&=,|=,^=,<<=,>>=。 - 单目运算符:
+(正),-(负),&(引用),*(指针),++,--,new,new[],delete,delete[]。 - 其它运算符:
[](下标),->(成员访问),()(函数调用),,(逗号)。
不能重载的运算符有:., .*, .->, ?:, ::以及sizeof类单词运算符。
3.2、限制
运算符重载有一定的限制:
- 操作数至少有一个为自定义类型。
- 运算符重载只能重载运算符的运算逻辑,无法改变运算符的固有特性。比如,无法自造运算符;无法改变运算符操作数数量,无法改变运算符优先级,无法改变运算符是左结合还是右结合,无法改变语法结构。
- 重载的运算逻辑应该合理,不应该重载
+, 进行减操作,当然这点编译器无法判断。
4、实战
编写一些实例巩固一下,注意个别运算符重载的细节。
4.1、++/–的前置与后置。
自增(自减)虽然是单目,但是有前置或后置两种用法,所以默认前置,使用int参数标记后置。用成员函数的方式重载前置++实现实部加+,后置++实现虚部加1,都返回改变后的值。
Complex& operator++(){ //前置++
++real;
return *this;
}
Complex& operator++(int){//后置++
++virt;
return *this;
}
这里返回引用的目的是支持连续调用。如果使用非成员函数的方式,需要增加第一个参数类型为Complex,函数签名为:
Complex& operator++(Complex& c); //前置
Complex& operator++(Complex& c,int); //后置
4.2、输入输出
输入输出<<, >>本来就是由位运算重载过来的。为了支持我们输入与输出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<<c.real<<'+'<<c.virt<<'j';
return output;
}
这里返回引用的目的也是支持连续调用,如cin>>c1>>c2;。重载之后,就可以实现输入输出,如:
Complex c1;
std::cin>>c1; //输入形如1+2j
std::cout<<c1<<std::endl; //输入形如1+2j
4.3、函数调用运算符()
()运算符其实是一个多元运算符,允许重载进行多个操作数的运算。 下面重载一个三个操作数(包括this)的。
Complex& Complex::operator()(int a,int b){
real+=a;
virt+=b;
return *this;
}
重载之后,便可以使用c1(3, 4);实现实部加3虚部加4的运算。可以发现,()像是一个与对象同名的函数。
各个运算符的重载就不详举了…原理都差不多。想看更多例子,可以看菜鸟教程。完。
本文介绍了C++中的运算符重载,讲解了如何通过运算符函数实现自定义类型的运算,包括重载+运算符进行复数相加,重载*运算符实现复数乘法,以及详细讨论了运算符重载的限制。此外,还通过实例演示了前置和后置自增运算符、输入输出重载以及函数调用运算符()的重载方法。
1万+

被折叠的 条评论
为什么被折叠?



