C++学习笔记:运算符重载
1.运算符重载引入
1.1 从函数重载说起
函数重载是在一定作用域内,多个相同名称但不同参数列表的函数重载。编译时由编译器根据实际调用时给的实参情况来判定本次实际用哪个函数,这个过程叫重载决策。
重载函数本质上就是多个独立函数,重载机制在编译时发生,运行时不参与。函数重载的意义就是避免我们给函数胡乱起名,方便编写类库覆盖所有可能操作,是一种语法糖。
1.2 什么是运算符重载
什么是运算符?比如+, - ,* ,/ ,%
等算术运算符和>, < ,==, !=
等关系运算符就是典型的可重载运算符(但不是所有的运算符都可以重载,譬如sizeof
)。运算符诞生于C
语言中,用来对变量进行某种预定义的运算操作,这种预定义是编译器预制好的,编译时会翻译对应到CPU机器码。
面向对象会带来新的问题:两个对象如何运算?
举个栗子:
Person a, b, c;
c = a + b;
此处的+
让编译器如何解读?
1.3 运算符重载示例
#include<iostream>
#include<string>
using namespace std;
class point_2d
{
public:
string name;
int x,y;
point_2d(string name,int x,int y):name(name),x(x),y(y){
};
void print(void)
{
cout<<name<<":x="<<x<<",y="<<y<<endl;
}
point_2d operator+(const point_2d&other)
{
return point_2d("default_name",other.x+this->x,other.y+this->y);
}
};
int main(int argc,char**argv)
{
point_2d a("a",10,20),b("b",22,33);
a.print();
b.print();
point_2d c=a+b; //c=a.operator+(b);
c.print();
return 0;
}
2.深度理解运算符重载
2.1 运算符重载的本质
表面上看运算符重载是对C++
源生运算符的意义在某个class
中做了重定义。本质上是运算符被映射到执行相应的成员函数,所以运算符重载其实是重定义对象的运算符所对应的函数。
2.2 运算符重载的意义
运算符重载是一种语法特性,C++
全面支持,Java
不支持,Python
有限度的支持。没有运算符重载照样写代码,所有操作全部通过显式调用相应成员函数来完成即可。运算符重载是一种语法糖,可以让代码“看起来更简洁,更优雅”,将复杂实现隐藏在类的内部。运算符重载机制加大了类库作者的工作量,减少了调用类库写功能的人的代码量,C++
支持运算符重载机制有其理念和历史原因,是对写法简洁和效率优秀的综合考量。
赋值运算符=
重载和引用结合,可有效提升代码效率,赋值运算符=重载时要注意有指针成员时会涉及浅拷贝和深拷贝,运算符重载一定程度上体现了C++
的多态性,因为同样的运算符在不同的class
中表现是不同的。
3.理解运算符重载的关键点
3.1 运算符重载规则
首先要记住、理解、并且能轻松认出写出运算符重载函数的格式、名称、对应的运算符。然后要理解在运算符重载函数中this
表示谁,参数表示谁,返回值对应谁,这个是重中之重。
point_2d c=a+b; //c=a.operator+(b);
在上面的代码中,a
对应this
,b
对应函数参数other
,a+b
的表达式的值对应函数返回值。
运算符重载总的规则:运算符左边的是this
,右边的是other
,运算符加操作数的整个表达式的返回值就是返回值。
3.2 运算符=的默认提供问题
在point_2d
类中并没有对=
进行重载,但是也可以使用c=a+b
。这是因为=
运算符有点特殊,编译器会提供一个默认的=
运算符的重载,所以代码里没有显式提供时也能用。如果自己显式写了=
运算符的重载函数,则会覆盖编译器自动提供的那一个。c = a;
等价于c.operator=(a);
,其中c
对应this
,a+b
对应other
。
3.3 总结
编译器会为每个自定义class
提供一个默认的赋值运算符的重载,而我们可以覆盖这个重载。盯紧表达式实际对应执行哪个函数,盯紧函数的参数是谁,this
是谁,返回值是谁,一切就都清楚了。
a+b
有可能不等于b+a
,这个也是运算符重载衍生出来的不好把控的风险问题。运算符重载给了类库作者非常大自由度,所以容易失控,实际中不乏写的很雷人的运算符重载。