C++学习之路(七),C++primer plus 第八章 函数探幽

								**第 8 章  函数探幽**
	本章内容包括:
				内联函数。
				引用变量。
				如何按引用传递函数参数。
				默认参数。
				函数重载。
				函数模板。
				函数模板具体化。

       通过第 7 章,了解到很多有关 C++ 函数的知识,但需要学习的知识还很多。C++ 还提供许多新的函数特性,使之有别于 C 语言。新特性包括内联函数、按引用传递变量、默认的参数值、函数重载(多态)以及模板函数。本章介绍的 C++ 语言基础上新增的特性,以前面各章都多,这是进入加加(++)领域重要一步。

8.1 C++ 内联函数

       内联函数是 C++ 为提高程序运行速度所做的一项改进。常规函数和内联函数之间和主要区别不在于编写方式,而在于 C++ 编译器
如何将它们组合到程序中。要了解内联函数与常规函数之间的区别,必须深入到程序内部。
  编译过程的最终产品是可执行程序------由一组机器语言指令组成。运行程序时,操作系统将这些指令载入到计算机内存中,因此每条指令都有特定的内存地址,计算机随后将逐步执行这些指令。有时(如有循环或分支语句时),将跳过一些指令,向前或向后跳到特定地址。常规函数也使程序跳到另一个地址(函数的地址),并在函数结束时返回。下面更详细地介绍这一过程的典型实现。执行到函数调用指令时,程序将在函数调用后立即存储该指令的内存地址,并将函数参数复制到堆栈(为此保留的内存块),跳到标记函数起点的内存单元,执行函数代码(也许还需将返回值放入到寄存器中),然后跳加到地址被保存的指令处,(这与阅读文章时停下来看脚注,并在阅读完脚注后返回到以前阅读的地方类似)。来回跳跃并记录跳跃位置意味着以前使用函数时,需要一定的开销。
  C++ 内联函数提供了另一种选择。内联函数的编码代码与其他程序代码“内联”起来了,也就是说,编译器将使用相应的函数代码远的函数调用。对于内联代码,程序无需跳到另一个位置处执行代码,再跳回来。因此,内联函数的运行速度比常规函数稍快,但代码是需要占用更多内存。如果程序在 10 个不同的地方调用同一个内联函数,则该程序将包
含该函数代码的 10 个副本(参见图 8.1)
Alt
   应有选择地使用内联函数。如果执行函数代码的时间比处理函数调用机制的时间长,则节省的时间将只占整个过程的很小一部分。如果代码执行时间很短,则内联调用就可以节省非内联调用使用的大部分时间。另一方面,由于这个过程相当快,因此尽管节省了该过程的大部分时间,但节省的时间绝对值并不大,除非该函数经常被调用。
  要使用这项特性。必须采取下述措施之一:
  在函数声明前加上关键字 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"
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值