**第 8 章 函数探幽**
本章内容包括:
内联函数。
引用变量。
如何按引用传递函数参数。
默认参数。
函数重载。
函数模板。
函数模板具体化。
通过第 7 章,了解到很多有关 C++ 函数的知识,但需要学习的知识还很多。C++ 还提供许多新的函数特性,使之有别于 C 语言。新特性包括内联函数、按引用传递变量、默认的参数值、函数重载(多态)以及模板函数。本章介绍的 C++ 语言基础上新增的特性,以前面各章都多,这是进入加加(++)领域重要一步。
8.1 C++ 内联函数
内联函数是 C++ 为提高程序运行速度所做的一项改进。常规函数和内联函数之间和主要区别不在于编写方式,而在于 C++ 编译器
如何将它们组合到程序中。要了解内联函数与常规函数之间的区别,必须深入到程序内部。
编译过程的最终产品是可执行程序------由一组机器语言指令组成。运行程序时,操作系统将这些指令载入到计算机内存中,因此每条指令都有特定的内存地址,计算机随后将逐步执行这些指令。有时(如有循环或分支语句时),将跳过一些指令,向前或向后跳到特定地址。常规函数也使程序跳到另一个地址(函数的地址),并在函数结束时返回。下面更详细地介绍这一过程的典型实现。执行到函数调用指令时,程序将在函数调用后立即存储该指令的内存地址,并将函数参数复制到堆栈(为此保留的内存块),跳到标记函数起点的内存单元,执行函数代码(也许还需将返回值放入到寄存器中),然后跳加到地址被保存的指令处,(这与阅读文章时停下来看脚注,并在阅读完脚注后返回到以前阅读的地方类似)。来回跳跃并记录跳跃位置意味着以前使用函数时,需要一定的开销。
C++ 内联函数提供了另一种选择。内联函数的编码代码与其他程序代码“内联”起来了,也就是说,编译器将使用相应的函数代码远的函数调用。对于内联代码,程序无需跳到另一个位置处执行代码,再跳回来。因此,内联函数的运行速度比常规函数稍快,但代码是需要占用更多内存。如果程序在 10 个不同的地方调用同一个内联函数,则该程序将包
含该函数代码的 10 个副本(参见图 8.1)
应有选择地使用内联函数。如果执行函数代码的时间比处理函数调用机制的时间长,则节省的时间将只占整个过程的很小一部分。如果代码执行时间很短,则内联调用就可以节省非内联调用使用的大部分时间。另一方面,由于这个过程相当快,因此尽管节省了该过程的大部分时间,但节省的时间绝对值并不大,除非该函数经常被调用。
要使用这项特性。必须采取下述措施之一:
在函数声明前加上关键字 inline;
在函数定义前加上关键字 inline.
通常的做法是省略原型,将整个定义(即函数头和所有的函数代码)放在本应提供原型的地方。
程序员请求将自己(内联函数不能递归),因此不将其作为内联函数dmj有些编译器没有启用或实现这种特性。
程序清单 8.1 通过内联函数 square()(计算参数的平方)演示了内联技术。注意到整个函数定义都放在一行中,但并不一定非得这样做。然而,如果函数定义占用多行(假定没有使用冗长的标识符),则将其作为内联函数就不太合适。
程序清单 8.1 inline.cpp
//inline.cpp -- using an inline function
#include <iostream>
// an inline function definition
inline double square (double x) {
return x * x; }
int main()
{
using namespace std;
double a, b;
double c = 13.0;
a = square(5.0);
b = square(4.5 + 7.5); // can pass expressions
cout << "a = " << a << ", b = " << b << "\n";
cout << "c = " << c;
cout << ", c squared = " << square(c++) << "\n";
cout << "Now c = " << c << "\n";
return 0;
}
程序运行结果如下:
输出表明,内联函数和常规函数一样,也是按值来传递参数的。如果参数为表达式,如 4.5 + 7.5,则函数将传递表达式的值(这里为 12)。这使得 C++ 的内联功能远远胜过 C 语言的宏定义(#define 宏定义),请参见旁注“内联与宏”。
尽管程序没有提供独立的原型,但 C++ 原型特性仍在起作用。这是因为在函数首次使用前出现的整个函数定义充当了原型。这意味着可以给 square()传递 int 或 long 值,将值传递给函数前。程序彼动将这个值强制转换为 double类型。
把代码的 inline去掉再运行:
说实话,写在这里并没有真正的理解到 inline的作用。用不用 inline效果是一样的????????,先多打几个问号
内联与宏
inline 工具是 C++新增的特性。 C语言使用预处理器语句 #define 来提供宏-------内联代码的原始实现。例如,下面是一个计算平方的宏:
#define SQUARE(X) XX
这并不是通过传递参数实现的,而是通过文本替换来实现的------X 是“参数”的符号标记。
a = SQUARE(5.0); is replaced by a = 5.0 5.0;
b = SQUARE(4.5 + 7.5); is replaced by b = 4.5 + 7.5 * 4.5 + 7.5;
d = SQUARE(c++); is replaced by d = c++c++;
上述示例只有第一个能正常工作。可以通过使用括号来进行改进 :
#define SQUARE(X) ((X)(X))
但仍然存在这样的问题,即宏不能按值传递。即使使用新的定义,SQUARE(C++)仍将c递增两次,但是程序清单 8.1 中的内联函数SQUARE()计算c的结果,传递它,以计算平方值,然后将C递增一次。这里的目的不是演示如何编码 C 宏,而是要指出。如果使用 C语言的宏执行了类似函数的功能,应考虑将它们转换为 C++ 内联函数。
后来晚上我睡前想了一下,本来不加 inline 它只能在调用的时候使用,而加上 inline 关键字后,此函数变成了内联函数,应该是酱紫吧,运行结果肯定一样的,只是运行此函数的方式发生了改变。
8.2 引用变量
C++ 新增了一种复合类型 ──引用变量。引用是已定义的变量的别名(另一个名称)。例如,如果将 twain 作为 clement 变量的引用,则可以交替使用 twian 和 clement 来表示该变量。那么,这种别名有何作用呢?是否能帮助那些不知道如何选择变量名的人呢?有可能,但引用变量的主要用途是用作函数的形参。通过将引用变量用作参数,函数将使用原始数据,而不是其副本。这样除指针之外,引用也为函数处理大型结构提供了一种灰常方便的途径,同时对于设计类来说,引用也是必不可少的。然而,介绍如何将引用用于函数之前,先介绍一下定义和使用引用的基本知识。请记住,下述讨论旨在说明引用是如何工作的,而不是其典型用法。
8.2.1 创建用引变量
前面讲过, C 和 C++ 使用 & 符号来指示变量的地址。C++ 给 & 符号赋予了另一个含义,将其用来声明引用。例如,要将rodents 作为rats 变量的别名,可以这样做:
int rats;
int &todents = rats; // make rodents an alias for rats ( 使 rodents【 rodents 】 作为 rats【老鼠; 耗子 】 的一个别名 ) 其中,& 不是地址运算符, 而是类型标识符的一部分。就像声明中的 char* 指的是指向char 的指针一样,int & 指的是指向 int 的引用。上述引用声明允许将 rats 和 rodents 与换 ---- 它们指向相同的值和内存 单元,程序清单 8.2 表明了这一点。
&(引用)是来传值的 == > 出现在变量声明语句中位于变量左边时,表示声明的引用,
例如:int &a; //声明一个 int 型的引用 a。
&(取地址运算符) == > 在给变量赋初始值时出现在等号右边或执行语句中,表示取对象的地址。
说得更加简单易懂点:
①引用在等号_左边,而取地址在等号右边,例如:
int a = 3; int &b = a; // 引用
int *p = &a // 取地址
②和类型在一起的是引用,和变在一起的是取地址,例如,同样如上,还有下例: int function( int &i) { } //引用
程序清单 8.2 firstref.cpp
// firstref.cpp -- defining and using a reference
#include <iostream>
int main()
{
using namespace std;
int rats = 101;
int & radents - rats; // rodents is a reference
cout << "rats = " << rats;
cout << ", rodents = " << rodents << endl;
rodents ++;
cout << "rats = " << rats;
cout << ", rodents = " << rodents << endl;
// some implementations require type casting the following
// addresses to type usingned
cout << "rats address = " << &rats;
cout << ", rodents address = " << &rodents << endl;
return 0;
}
请注意,下述语句中的 & 运算符不是地址运算符,而是将 rodents 的类型声明为 Int &,即指向 int 变量的引用:
int & rodents = rats;
但下述语句中的 & 运算符是地址运算符,其中 &rodents 表示 rodents 引用的变量的地址:
cout << “, rodents address = ” << &rodents << endl;
下面是程序运行结果:
从图中可知,rats 和 rodents 的值和地址都完全相同(具体的地址和显示格式随系统而异)。将 rodents 加 1 将影响这两个变量。更准确地说,rodents ++ 操作将一个有两个名称的变量加 1。(同样,虽然该 示例演示了引用是如何工作的,但并没有说明引用的典型用途,即作为函数参数,具体地说是结构和对象参数,稍后将介绍这些用法)。
对于 C 语言用户而言,首次接触到引用可能 也会有些困惑,因为这些用户很自然地会想到指针,但它们之间还是有区别的。例如,可创建指向 rats 的引用和指针:
int rats = 101;
int & rodents = rats; // rodents is a reference
int * prats = &rats; //prats is a pointer
这样,表达式 rodents 和 *prats都可以同 rats 互换,而表达式 &rodents 和 prats 都可以同 &rats 互换。从这一点来说,引用看上去很像伪装表示的指针(其中,**解除引用运算符被隐式理解)。实际上,引用还是不同于指针的。除了表示法不同外,还有其他的差别。例如,差别之一是,必须在声明引用时将其初始化,而不能像指针那样,先声明,再赋值:
int rat;
int & rodent;
rodent = rat; // No,you can’t do this.
注意:必须在声明引用变量时进行初始化。
引用更接近 const 指针,必须在创建时进行初始化,一旦与某个变量关联起来,就将一直效忠于它。也就是说:
int & rodents = rats;
实际上是下述代码的伪装表示:
int * const pr = &rats;
其中,引用 rodents 扮演的角色与表达式 *pr相同。
程序清单 8.3 演示了试图将 rats 变量的引用改为 bunnies 变量引用时,将发生的情况。
程序清单 8.3 sceref.cpp
// scref.cpp -- defining and using a reference
#include <iostream>
int main()
{
using namespace std;
int rats = 101;
int & rodents = rats; // rodents is a reference
cout << "rats = " << rats;
cout << ", rodents = " << rodents << endl;
cout << "rats address = " << &rats;
cout << ", rodents address = " << &rodents << endl;
int bunnies = 50;
rodents = bunnies; // can we change the reference?
cout << "bunnies = " << bunnies;
cout << ", rats = " << rats;
cout << ", rodents = " << rodents << endl;
cout << "bunnies address = " << &bunnies;
cout << ", rats address = " << &rats;
cout << ", rodents address = " << &rodents << endl;
return 0;
}
下面是程序运行结果:
最初,rodents 引用的是 rats,但随后程序试图将 rodents 作为 bunnies的引用:
rodents = bunnies;
咋一看,这种意图暂时是成功的,因为 rodents 的值从 101 变成了 50.但仔细研究将发现,rats 也变成了50,同时 rats 和 rodents的地址是相同的,而该地址与 bunnies 的地址不同。由于 rodents 是 rats 的别名,因此下述赋值语句与下面的语句等效:
rats = bunnies;
也就是说,这意味着 ”将 bunnies 变量的赋值给 rats 变量“。简而言之,可以通过初始化声明来设置引用,但不能通过赋值来设置。
假设程序员试图这样操作:
int rats = 101;
int * pt = &rats;
int & rodents = *pr;
int bunnies = 50;
pr = &bunnies;
将 rodents 初始化为 *pt使用 rodents 指向 rats。接下来将 pt 改为指向 bunnies,并不能改变这样的事实,即 rodents 引用的是 rats。
于黄冈办公室
8.2.2 将引用用作函数参数
引用经常被用作函数参数,使得函数中的变量名成为调用程序中的变量的别名。这种传递参数的方法称为按引用传递。按引用传递允许被
调用的函数能够访问函数中的变量。C++ 新增的这项特性是对 C语言的超越,C 语言只能按值传递。按值传递导致被调用函数使用调用
程序的值的拷贝(参见图 8.2)。当然,C 语言也允许避开按值传递的限制,采用按指针传递的方式。
现在我们通过一个常见的计算机问题 ---- 交换两个变量的值,对使用引用和使用指针做一下比较。交换函数必须能够修改调用程序中的变量的值。这意味着按值传递变量将不管用,因为函数将交换原始变量副本的内容,而不是变量本身的内容。但传递引用时,函数将可以使用原始数据。另一种方法是,传递指针来访问原始数据。程序清单 8.4 演示了这三种方法。其中包括一种不可行的方法,以便能够对这些方法进行比较。
程序清单 8.4 swaps.cpp
// swaps.cpp -- swapping with references and with pointers
#include <iostream>
void swapr(int & a, int & b); // a, b are aliases for ints
void swapp(int * p, int * q); // p, q are addresses of ints
void swapv(int a, int b); // a, b are new veriables
int main()
{
using namespace std;
int wallet1 = 300;
int wallet2 = 350;
cout << "wallet1 = $" << wallet1;
cout << " wallet2 = $" << wallet2 << endl;
cout << "Using references to swap contents: \n";
swapr(wallet1, wallet2); // pass variables
cout << "wallet1 = $" << wallet1;
cout << " wallet2 = $" << wallet2 << endl;
cout << "Using pointers to swap contents again:\n";
swapp(&wallet1, &wallet2); // pass addresses of variables
cout << "wallet1 = $" << wallet1;
cout << " wallet2 = $" << wallet2 << endl;
cout << "Trying to use passing by value:\n";
swapv(wallet1, wallet2); // pass values of variables
cout << "wallet1 = $" << wallet1;
cout << " wallet2 = $" << wallet2 << endl;
return 0;
}
void swapr(int &a, int & b) // use references
{
int temp;
temp = a; // use a, b for values of variables
a = b;
b = temp;
}
void swapp(int * p, int * q) // use pointers
{
int temp;
temp = *p; // us *p, *q for values of variables
*p = *q;
*q = temp;
}
void swapv(int a, int b) // try using value
{
int temp;
temp = a; // use a, b for value of variables
a = b;
b = temp;
}
程序运行如下:
引用和指针方法都成功的交换了两个钱夹(wallet)中的内容。而按值传递的方法没能完成这项任务。
程序说明
首先来看程序清单 8.4 中每个函数是如何被调用的:
swapr(wallet1, wallet2); // pass variables (传递变量)
swapp(&wallet1, &wallet2); // pass addresses of variables (传递变量的地址)
swapv(wallet1, wallet2); // pass values of variables (传递变量的值)
按引用传递(swapr(wallet1, wallet2)) 和按值传递(swapv(wallet1, wallet2)) 看起来相同。只能通过原型或函数定义才能知道 swapr() 是按引用传递的。然则,地址运算符 & 使得按地址传递(swapp(&wallet1, &wallet2))一目了然(类型声明 int * p表明,p 是一个 int 指针,因此与 p 对应的参数应为地址,如 &wallet1)。
接下来,比较函数swapr()(按引用传递) 和 swapv()(按值传递)的代码,唯一的外在区别是声明函数参数的方式不同:
void swapr(int & a , int & b)
void swapv(int a, int b)
当然还在内在区别,在 swapr()中,变量 a 和 b 是 wallet1 和 wallet2 的别名,所以交换 a 和 b 的值相当于交换 wallet1 和wallet2 的值;会是在 swapv()中,变量 a 和 b 是复制了 wallet1 和 wallet2 的值的新变量,因此交换 a 和 b 的值并不会影响 wallet1 和 wallet2 的值。
最后,比较函数 swapr()(传递引用)和 swapp()(传递指针)。第一个区别是声明函数参数的方式不同:
void swapr(int & a, int & b)
void swapp(int * p, int * b)
另一个区别是指针版本需要函数使用 p 和 q 的整个过程中使用解除引用运算符 *。
前面说过,应在定义引用 变量时对其进行初始化。函数调用使用实参初始化形参。因此函数的引用参数被初始化为函数调用传递的参数。也就是说,下面的函数调用将形参 a 和 b 分别初始化 wallet1 和wallet2:
swapr(wallet1, wallet2);
人是不能懒惰,一懒起来什么都不想写,继续…、
8.2.3 引用的属性和特别之处
使用引用参数时,需要了解其一些特点,首先,看程序清单 8.5,它使用两个函数来计算参数的立方,其中一个函数接受 double 类型的参数,另一个接受 double 引用,我们有意将计算立方的代码编写得比较奇怪。
程序清单 8.5 cubes.cpp
// cubes.cpp -- regular and reference arguments
#include <iostream>
double cube (double a);
double refcube(double &ra);
int main()
{
using namespace std;
double x = 3.0;
cout << cube( x );
cout << " = cube of " << x << endl;
cout << refcube( x );
cout << " = cube of " << x << endl;
return 0;
}
double cube(double a)
{
a *= a * a;
return a;
}
double refcube(double &ra)
{
ra *= ra * ra;
return ra;
}
程序运行结果为:
refcube() 函数修改了 main() 中的 x 值,而 cube()没有,这提醒我们为何通常按值传递。变量 a 位于 cube()中,它被初始化为 x 的值,但修改 a 并不会影响 x。但由于 refceub()使用了引用参数,因此修改 ra ,实际上也就是修改 x。如果程序员的意图是使用传递给它的信息,而不对这些信息进行修改,同时又想使用引用,则应使用常量引用,例如,在例子中,应该在函数原型和函数头中使用 const:
double refcube(const double &ra);
如果这样做,当编译器发现代码修改了 ra 的值时,将生成错误信息。
顺便说一句,如何要编写类似于上述示例的函数(即使用基本数值类型),应采用按值传递的方式,而不要采用按引用传递的方式。当数据比较大(如结构和类)时,引用参数将很有用,稍后便会明白这一点。
按值传递的函数,如何程序清单 8.5 中的函数 cube(),可使用多种类型的实参。
题外语,实参和形参:
实参:可以是常量、变量、表达式、函数等, 无论实参是何种类型的量,在进行函数调用时,它们都必须具有确定的值, 以便把这些值传送给形参。 因此应预先用赋值:
比如函数 int fun(int a,int c){some operation;}
a和c都是形参。
当我调用函数fun时,例如:
int n,i=1,j=2;
n=fun(i,j);
调用fun(i,j)形式中的i,j就是实参。
回来继续之。例如,下面的调用都是合法的:
double z = cube(x + 2.0); // evaluate x + 2.0, pass value < 计算 x + 2.0,传递这个值 >
z = cube(8.0); // pass the value 8.0 < 传递这个值 8.0>
int k = 10;
z = cube(k); // convert value of k to double, pass value < 将k值转换为double类型,传递这个值 >
double yo[ 3 ] = { 2.2, 3.3, 4.4 };
z = cube (yo[ 2 ]); // pass the value 4.4 < 传递这个值 4.4>
如果将与上面类似的参数传递给接受引用参数的函数,将会发现,传递引用的限制更严格。毕竟,如果 ra 是一个变量的别名,则实参应是该变量。下面的代码不合理,因为表达式 x + 3.0 并不是变量:
double z = refcube(x + 3.0 ); // should not compile < 不能编译 >
例如,不能将值赋给该表达式:
x + 3.0 = 5.0; // nonsensical < 荒谬的;无意义的 >
如果试图使用像 refcube(x + 3.0)这样的函数启用,将发生什么情况呢?在现代的 C++中,这是错误的,大多数编译器都将会指出这一点;而有些较老的编译器将发出这样的警告:
Warning: Temporary used for parameter ‘ra’ in call to refcube(double &)
之所以做出这种比较温和的反应是由于早期的 C++ 确实允许将表达式传递给引用变量。有些情况下,仍然是这样做的。这样做的结果如下:由于 x + 3.0 不是 double 类型的变量,因此程序将创建一个临时的无名变量,将将其初始化为表达式 x + 3.0 的值,然后,ra 将成为该临时变量的引用。下面详细讨论这种临时变量,看看什么时候创建它们,什么时候不能创建。
完了,这饭还真不能吃,嘴里的泡又变大了,杯具了.
临时变量、引用参数和 const
如果实参与引用参数不匹配,C++ 将生成临时变量。当前,仅当参数为 const 引用时,C++ 才允许这样做,但以前不是这样。下面来看看何种情况下,C++ 将生成临时变量,以及为何对 const 引用的限制是合理的
首先,什么时候将创建临时变量?如果引用参数是 const ,则编译器将在下面两种情况下生成临时变量:
实参的类型正确,但不是左值;
实参的类型不正确,但可以转换为正确的类型。
左值是什么呢?,左值参数是可被引用的数据对象,例如,变量、数组元素、结构成员、引用和解除引用的指针都是左值。非左值包括字面常量(用引号括起的字符串除外,它们由其地址表示)和包含多项的表达式。在 C 语言中,左值最初指的是可出现在赋值语句左边的实体,但这是引入关键字 const 之前的情况。现在,常规变量和 const 变量都可视为左值,因为可通过地址访问它们。但常规变量属于可修改的左值,而 const 变量属于不可修改的左值。
回到前面的示例。假设重新定义了 refcube(),使其接受一个常量引用参数:
double refcube(donst double &ra)
{
return ra * ra * ra;
}
现在考虑下面代码:`
double side = 3.0;
double * pd = &side;
double &rd = side;
long edge = 5L;
double lens[ 4 ] = {
2.0, 5.0 10.0, 12.0 };
double c1 = refcube(side); // ra is side
double c2 = refcube(lens[ 2 ]); // ra is lens[ 2 ]
double c3 = refcube(rd); // ra is rd is side
double c4 = refcube(*pd); // ra is * pd is side
double c5 = refcube(edge); // ra is temporary varibale
double c6 = refcube(7.0); // ra is temporary varibale
double c7 = refcube(sie + 10.0); // ra is temporary varibale
参数 side、lens[ 2 ]、rd 和 *pd 都是有名称的、double 类型的数据对象,因此可以为其创建引用,而不需要临时变量(还记得吗,数组元素的行为与同类型的变量类似)。然而,edge 虽然是变量,类型却不正确,double 引用不能指向 long。另一方面,参数 7.0我列个大艹,网页抽风,其它网页都没问题,这个网页突然崩溃,弄得我之后到程序清单 8.6 都没有保存,又是重新写一遍!!! 和 side + 10.0 的类型都正确,但没有名称,在这些情况下,编译器都将生成一个临时匿名变量,并让 ra 指向它。这些临时变量是只在函数调用期间存在,此后编译器便可以随意将其删除。
那么为什么对于常量引用,这种行为是可行的,其他情况下却不行的呢?对于程序清单 8.4 中的函数 swapr():
void swapr(int & a, int & b) // us references
{
int temp;
temp = a; // use a, b for values of variables
a = b;
b = temp;
}
如果在早期 C++较宽松的规则下,执行下面的操作将发生什么情况呢?
long a = 3, b = 5;
swapr(a, b);
这里的类型不匹配,因此编码器将创建两个临时 int 变量,将它们初始化为 3 和 5,然后交换临时变量的内容,而 a 和 b 保持不变。
简而言之,如果接受引用参数的函数的意图是修改作为参数传递的变量,则创建临时变量将阻止这种意图的实现。解决方是,禁止创建临时变量,现在的 C++ 标准正是这样做的(然而,在默认情况下,有些编译器仍将发出警告,而不是错误消息,因此如果看到了有关临时变量的警告,请不要忽略)。
现在来看 refcube()函数。该函数的目的只是使用传递的值,而不是修改它们,因此临时变量不会造成任何不利的影响,反而会使函数在可处理的参数种类方面更通用。因此,如果声明交换引用指定为 const,C++将在必要时生成临时变量,实际上,对于形参为 const 引用的 C++ 函数,如果实参不匹配,则其行为类似于按值传递,为确保原始数据不被修改,将使用临时变量来存储值。
注意:如果函数调用的参数不是左值或与相应的 const 引用参数的类型不匹配,则 C++将创建类型正确的匿名变量,将函数调用的参数的值传递给该匿名变量,并让参数来引用该变量。
应尽可能使用const
将引用参数声明为常量数据的引用的理由有三个:
- 使用 const 可以避免无意中修改数据的编程错误;
- 使用const 使函数能够处理 const 和非 const实参,否则将只能接受非 const 数据;
- 使用 const 引用使函数能够正确生成并使用临时变量。
因此,应尽可能将引用形参声明为 const。
C++新增了另一种引用 ---- 右值引用(rvalue reference)。这种引用可指向右值。是使用 && 声明的:
double && rref = std :: sqrt(36.00);// not allowed fo double &
double j = 15.0;
double && jref = 2.0* j + 18.5; // not allowed fo double &
std :: cout << rref << '\n'; // display 6.0
std :: cout << jref << '\n'; // display 48.5
新增右值引用的主要目的是,让库设计人员能够提供有些操作的更有效实现。第 18 章将讨论如何使用右值来实现移动语义(move semantics)。以前的引用(使用 & 声明的引用)现在称为左值引用。
8.2.4 将引用用于结构
引用非常适合于结构和类(C++的用户定义类型)。确实,引入引用主要是为了用这些类型的,而不是基本的内置类型。
使用结构引用参数的方式是使用基本变量引用相同,只需在声明结构参数时使用引用运算符 & 即可。例如,假设有如下结构定义:`
struct free_throws
{
std :: string name;
int made;
int attempts;
float percent;
};
则可以这样编写函数原型,在函数中将指向结构的引用作为参数:
void set_pc(free_throws & ft);// use a reference to a structure
如果不希望函数修改传入的结构,可使用 const:
void display(const free)throws & ft); //don't allow changes to structure
程序清单 8.6 中的程序正是这样做的。它还通过让函数返回指向结构的引用添加了一个有趣的特点,这与返回结构有所不同,对此。有一些需要注意的地方,稍后将进行介绍。
程序清单 8.6 strtref.cpp
// strc_ref.cpp -- using structure references
#include <iostream>
#include <string>
struct free_throws
{
std:: string name;
int made;
int attempts;
float percent;
};
void display(const free_throws & ft);
void set_pc(free_throws & ft);
free_throws & accumulate(free_throws &target, const free_throws & source);
int main()
{
// partial initializations - remaining menbers set to 0
free_throws one = {
"Ifelsa Branch", 13, 14 };
free_throws two = {
"Andor Knott", 10, 16 };
free_throws three = {
"Minnie Max", 7, 9 };
free_throws four = {
"Whily Looper", 5, 9 };
free_throws five = {
"Long Long"