
序言
函数重载是C++在C语言基础上新增的特性,函数重载
指的是可以有多个同名的函数,这就像你可以在学校以一个学生的身份读书,也可以在家以一个子女的身份帮爸妈干活。函数重载有着十分重要的意义,下面是其中几点:
- 试想如果没有函数重载机制,如在C中,你必须要这样去做:为这个print函数取不同的名字,如print_int、print_string。这里还只是两个的情况,如果是很多个的话,就需要为实现同一个功能的函数取很多个名字,如加入打印long型、char*、各种类型的数组等等。这样做很不友好!
- 类的构造函数跟类名相同,也就是说:构造函数都同名。如果没有函数重载机制,要想实例化不同的对象,那是相当的麻烦!
- 操作符重载,本质上就是函数重载,它大大丰富了已有操作符的含义,方便使用,如+可用于连接字符串等!
下面我们介绍一些关于重载的基础知识。
基本认识
函数重载的关键是函数的参数列表,也叫做函数特征标
,如果两个函数的参数数目和类型都相同,同时参数的排列顺序也相同,变量名是无关紧要的,C++允许定义名称相同的函数,前提是它们特征标不同,例如,可以定义以下一系列函数:
void print(const char *str, int width);
void print(double d, int width);
void print(long l, int width);
void print(int i, int width);
void print(const char *str);
使用print函数的时候,编译器将根据所采取的用法使用有相应特征标的原型,如下所示:
print("apple", 5);
print("apple");
print(1999.0, 10);
print(1999, 12);
print(1999L, 15);
在使用被重载的函数时,要确保参数类型是正确的
,例如下面的语句:
unsigned int year = 2001;
print(year, 6);
这里的调用方式并不会有函数与它匹配,但C++也不会直接报错,他会首先将所给数据进行强制类型转换变成可以传送的数据,但是我们发现,这里有三个函数可以接收这种转换,所以就产生了冲突,这里C++才会报错。
一些特征标不同的函数,但在实际使用过程中存在歧义,所以C++也是不允许的
,如下所示:
double cube(double x);
double cube(double &x);
在如下的调用过程中,C++并不能区分它们
m = cuble(x);
是特征标,而不是函数类型使得可以对函数进行重载
,例如下面的两个声明是互斥的
long gronk(int n, float n);
double gronk(int n, float n);
C++不允许这样的重载方式,返回值类型可以不同,但是特征标也必须不同。
long gronk(int n, float n);
double gronk(float n, float n);
重载引用参数
类设计和STL经常使用引用参数,因此知道不同引用类型的重载很有用。请看下面三个原型:
void sink{double & r1};
void sank{const double & r2};
void sunk{double && r3};
左值引用参数r1与可修改的左值参数(如double变量)匹配;const左值引用参数r2与可修改的左值参数、const左值参数和右值参数(如两个double值的和)匹配;最后,左值引用参数r3与左值匹配。注意到与r1或r3匹配的参数都与r2匹配。这就带来了一个问题:如果重载使用这三种参数的函数,结果将如何?答案是将调用最匹配的版本
。
void staff(double & rs);//匹配可修改的左值
void staff(const double &rcs);//匹配右值和常量左值
void stove(double & r1);//匹配可修改的左值
void stove(const double & r2);//匹配常量左值
void stove(double && r3);//匹配右值
下面是一些使用示例:
double x = 55.5;
const double y = 32.0;
stove(x);//调用 stove(double &)
stove(y);//调用 stove(const double &)
stove(x + y);//调用 stove(double &&)
我们可以看到,上面示例的调用方式完全复合调用最匹配的版本
这个原则。
如何使用函数重载
重载虽好,可不要乱用哦!!仅当函数基本上执行相同的任务,但使用不同形式的数据时,才使用函数重载。
名称修饰
C++如何跟踪每一个重载函数呢?在编译阶段,C++编译器会执行一种叫做名称修饰的操作,它根据函数中指定的形参类型对每个函数名进行加密。至于怎么加密,不同的编译器会有不同的机制。
这个就有点像python中的变量私有化技术:name mangling,在我的这篇文章的末尾关于python中局部变量的实现中有简要介绍。
结论:函数重载具有十分重要的意义,它极大地丰富了编程语言的功能。