使用函数重载创建函数使得用户能够使用多个同名的函数,他们完成不同的工作,使用不同的参数列表,但是函数名相同。
重载函数就像是有多种含义的动词。重载函数的关键是函数的参数列表—也称为函数特征标。如果两个函数的参数的数目和类型相同,同时参数的排列顺序也相同,则它们的特征标相同,而变量名是无关紧要的。例如,可以定义一组原型如下的 print() 函数:
void print(const char *str, int width); // #1 void print(double d, int width); // #2 void print(long l, int width);// #3 void print(int i, int width); // #4 void print(const char* str); // #5
而使用 print() 函数时,编译器将根据所采取的用法使用有相应特征标的原型:
print("Pancakes", 15); // use #1 print("Syrup"); // use #5 print(1999.0, 10); // use #2 print(1999, 12); // use #4 print(1999L, 15); // use #3
当输入的参数类型和任意一个重载函数都不匹配时,c++ 将试图使用标准类型转换强制进行匹配。例如:
unsigned int year = 2000; print(year, 5);
因为重载函数 print() 没有第一个参数是 unsinged 类型的,因此 c++ 尝试进行类型强制转换。如果 print() 函数只有 #2 (没有 #3 和 #4)类型,那么将直接使用 #2。但是 print() 的 #2、3、4 的第一个参数都是数字,这样 c++ 不能强制转换,将拒绝执行代码。
double cube(double x); double cube(double &x);
当输入参数 x,与这两个原型都相互匹配。
void dribble(char * bits); void dribble(const char *cbits); void drip(char* bits); const char p1[10] = "wang"; char p2[10] = "wang"; dribble(p1); dribble(p2); drip(p1); no match
注意到 dribble() 函数可以输入 const 和非 const 参数,但是 drip() 不能输入 const 参数。因为不能将 const 值赋给非 const 值。
long gronk(int n, float m); double gronk(int n,float m);
以上两个函数时互斥的,它们并非重载函数,但是函数名相同,c++ 将不知道如何使用。
类设计和 STL 经常使用引用参数,因此知道不同引用类型的重载很重要,请看以下三个原型:
void sink(double & r1); // 与可修改的左值参数匹配 void sank(const double & r2); // 与可修改的左值参数、const 左值参数和右值参数匹配 void sunk(double && r3); // 与右值匹配
注意到与 r1 和 r3 匹配的参数都与 r2 匹配。如果重载使用这三种参数的函数,结果如何?答案是将调用最匹配的版本:
void stove(double & r1); void stove(const double &r2); void stove(double && r3); double x = 55.5; const double y = 32.0; stove(x); // calls stove(double &) stove(y); // calls stove(const double &) stove(x+y); // calls stove(double&&)
以上程序中,如果没有定义函数 stove(double &&), stove(x+y) 将调用 stove(const double &)。
应当秉持一个原则:仅当函数基本上执行相同的任务,但使用不同形式的数据时,才使用函数重载。
1、左值和右值的概念
C++中左值(lvalue)和右值(rvalue)是比较基础的概念,虽然平常几乎用不到,但C++11之后变得十分重要,它是理解 move/forward 等新语义的基础。
左值与右值这两个概念是从 C 中传承而来的,左值指既能够出现在等号左边,也能出现在等号右边的变量;右值则是只能出现在等号右边的变量。
int a; // a 为左值 a = 3; // 3 为右值
左值是可寻址的变量,有持久性;
右值一般是不可寻址的常量,或在表达式求值过程中创建的无名临时对象,短暂性的。
左值和右值主要的区别之一是左值可以被修改,而右值不能。
2、左值引用和右值引用
左值引用:引用一个对象;
右值引用:就是必须绑定到右值的引用,C++11中右值引用可以实现“移动语义”,通过 && 获得右值引用。
int x = 6; // x是左值,6是右值 int &y = x; // 左值引用,y引用x int &z1 = x * 6; // 错误,x*6是一个右值 const int &z2 = x * 6; // 正确,可以将一个const引用绑定到一个右值 int &&z3 = x * 6; // 正确,右值引用 int &&z4 = x; // 错误,x是一个左值
右值引用和相关的移动语义是C++11标准中引入的最强大的特性之一,通过std::move()可以避免无谓的复制,提高程序性能。