写点什么

高效学 C++|函数参数的引用传递和函数重载

作者:TiAmo
  • 2023-01-28
    江苏
  • 本文字数:2654 字

    阅读完需:约 9 分钟

高效学 C++|函数参数的引用传递和函数重载

C++语言中函数的声明形式如下:

返回值类型 函数名(参数表);
复制代码

如声明将两个整型参数相加的函数 add()的程序如下:

int add(int x, int y);
复制代码

下面从函数参数的引用传递、函数重载两个方面介绍 C++语言中的函数。

01、引用传递

引用是一种特殊的声明,可以用来限定变量的类型。如果在声明一个变量的同时将它声明为另一个变量的引用,则意味着这两个变量等同于一个变量,即声明为引用的变量是它所引用的变量的别名。下面的程序中将变量 ri 声明为变量 i 的引用。

int i = 0, j = 1;
int & ri = i; //ri是一个int类型的引用,它引用的变量是int类型的i
ri = 10; //相当于i = 10
ri = j; //相当于i = j
复制代码

使用引用时必须注意下面两个问题。

(1)定义一个引用时,必须同时对它初始化,即明确它所引用的变量。

(2)引用类型的变量所引用的那个变量只能在初始化时指定,指定之后不能修改(实际上也没法修改)。

实际上,编译器对引用类型的变量自动地按照指针常量的形式进行了变换,编译器变换上面声明引用变量 ri 的语句如下:

int * const ri = &i;
复制代码

这样,就可以很清楚地看到 ri 是一个常变量,该常变量是一个指针常量,并且该指针指向一个 int 型数据。因为 ri 是常变量,所以声明时必须要初始化,也因此不能在声明之后修改它的值。这就很好地解释了为什么对引用类型的变量有前面两条的限制。

同理,编译器将前面给引用变量 ri 赋值的语句自动地改写为:

*ri = 10;
*ri = j;
复制代码

综上,编译器把“引用”改成了“指针”,所做的变换如下:

(1)将引用运算符“&”转换成指针运算符“* const”。

(2)在定义引用类型的变量时对右值取地址。

(3)对于函数的形参,如果它是引用类型,则形实结合时对实参取地址。

(4)对于使用引用变量的语句,在引用变量前加“*”。

总之,编译器自动地把“引用”改成了“指针常量”。

使用引用类型可使程序变得简洁、容易理解。使用引用类型的形参如例 1 所示。在该例中,swap 函数完成两个数据的交换:函数中是交换形参的值,但由于形参是引用类型,所以实际上完成了实参的交换。在本例中,交换前变量 x 等于 5,变量 y 等于 10;交换后变量 x 等于 10,变量 y 等于 5。

【例 1】 使用引用类型的形参。

1.  #include<iostream>2.  using namespace std;3.  void swap(int & a, int& b)4. {5.     int t;6.     t = a;7.     a = b;8.     b = t;9.  } 10.  int main()11. {12.     int x(5), y(10);13.     cout << "x = " << x << " y = " << y <<endl;14.     swap(x,y);15.     cout << "x = " << x << " y = " << y <<endl;16.     return 0;17.  }
复制代码

例 2 说明了函数返回引用类型的情况,此时可直接给函数调用后的返回值赋新值:变量 x 和变量 y 的初值分别是 2 和 5;在调用了 larger()函数之后给其返回值加 5,由于变量 y 的初值较大且 larger()函数的形参和返回值都是引用类型,所以实现了将变量 y 的值增 5 的功能,因此程序最后一行的输出为“x = 2 y = 10”。

【例 2】 函数返回引用类型。

1.  #include<iostream>2.  using namespace std;3.  int & larger(int & a, int & b)4.  {5.     return a > b ? a : b;6.  }7.  int main()8. {9.     int x = 2, y = 5;10.     cout << "x = " << x << " y = " << y <<endl;11.     cout << (larger(x, y) += 5) << endl;12.     cout << "x = " << x << " y = " << y <<endl;13.     return 0;14.  }
复制代码


 函数返回引用类型的数据是经常用到的,因为返回值经常需要作为左值。需要注意的是,不能将非静态局部变量按引用类型返回,因为在函数返回后,该局部变量已经不存在了,从而返回值也不能作为左值。比如例 3 中,将 larger()函数改为如下形式时将会出现警告。

int & larger(int & a, int & b){   int i = a > b ? a : b;   return i; //因为i为局部变量且返回值为引用类型,所以编译时会有警告}
复制代码

两种使用引用传递的主要情形如下:

(1)用于传递大量数据。因为引用传递被自动转换为指针传递,这能够减少复制数据的开销。

(2)用于返回一个内存空间作为左值。比如上面的 larger()函数,为了能够给其返回值赋值,需要使用引用(或采用相应的指针形式,但比较麻烦)。

02、函数重载

在面向对象程序设计中,成员函数是对类的行为的描述。对于同一个类,存在这样一种情况:虽然它执行的动作的称谓是相同的,但在不同的情况下执行的过程却是不同的。对于普通的函数也是如此。比如,已知两个数,要求把它们加起来。此时,如果这两个数是整数,那么可以用整数的加法运算完成计算;如果已知的数是虚数,那么可以用虚数的加法运算来完成计算。对于这样的“把两个数相加”的要求,可以把它们抽象成函数。不过,由于执行过程不同,在 C 语言中需要使用不同的函数名来完成,如下面的函数声明所示(假设 complex 是已定义好的表示虚数的类型)。

int add_int(int a, int b);complex add_complex(complex a, complex b);
复制代码

显然,这样设计不利于阅读和使用。为解决此类问题,在 C++语言中引入了函数重载的概念,即允许声明同名的函数。对于一个函数调用,编译器根据实参和形参的类型、个数及顺序自动确定调用哪个同名函数。比如上面两个函数可重新声明如下:

int add(int a, int b);
complex add(complex a, complex b);
复制代码

在调用函数时,根据实参的类型来选择调用哪个函数:如果实参是整型,则调用第一个函数版本;如果实参的类型是虚数,则调用第二个函数版本。

实际上,编译器在编译 C++语言的源程序时自动重命名了重载函数,重命名的方式大致是函数名加上参数类型列表,如上面两个函数会被重命名为类似于下面的形式。

int add_int_int(int a, int b);complex add_complex_complex(complex a, complex b);
复制代码

而对于调用语句“add(1, 2);”则会自动变为“add_int_int(1, 2);”。通过这种方式,编译器能够确定需要调用函数的哪个重载形式。

从上面的程序也可看出,在使用函数重载时要注意:尽管函数名可以相同,但形参的类型、个数及顺序一定要有不同,否则编译器就无法确定到底该调用哪个函数了。另外,函数的返回值类型不能用来区分函数的重载形式,因为在调用函数时常常不关心返回值,从而编译器也无法利用返回值的类型来确定该调用哪个函数。示例程序如下:

int   add(int x, int y){ ... } //第一个函数float add(float x, float y){ ... } //参数类型不同,与第一行的函数构成重载int   add(int x, int y, int z){ ... } //参数个数不同,与前两行的函数构成重载int   add(int a, int b){ ... } //与第一行的函数只是形参名不同,重载出错void  add(int x, int y){ ... } //不以返回值区分函数,重载出错
复制代码


发布于: 刚刚阅读数: 3
用户头像

TiAmo

关注

有能力爱自己,有余力爱别人! 2022-06-16 加入

CSDN全栈领域优质创作者;阿里云创作者社区专家博主、技术博主、星级博主;华为云享专家;

评论

发布
暂无评论
高效学 C++|函数参数的引用传递和函数重载_c++_TiAmo_InfoQ写作社区