8.3.2 返回const值

对返回值来讲,存在一个类似的道理,即如果一个函数的返回值是一个常量(const):

8.3.2 返回const值 - 图1

这就约定了函数框架里的原变量不会被修改。另外。因为这是按值返回的,所以这个变量被制成副本,使得初值不会被返回值所修改。

首先,这使const看起来没有什么意义。可以从这个例子中看到:按值返回const明显失去作用:

8.3.2 返回const值 - 图2

8.3.2 返回const值 - 图3

对于内建类型来说,按值返回的是否是一个const,是无关紧要的,所以按值返回一个内建类型时,应该去掉const,从而不使客户程序员混淆。

当处理用户定义的类型时,按值返回常量是很重要的。如果一个函数按值返回一个类对象为const时,那么这个函数的返回值不能是一个左值(即它不能被赋值,也不能被修改)。例如:

8.3.2 返回const值 - 图4

f5()返回一个非const X对象,然而f6()返回一个const X对象。仅仅是非const返回值能作为一个左值使用,因此,当按值返回一个对象时,如果不让这个对象作为一个左值使用,则使用const很重要。

当按值返回一个内建类型时,const没有意义的原因是:编译器已经不让它成为一个左值(因为它总是一个值而不是一个变量)。仅当按值返回用户定义的类型对象时,才会出现上述问题。

函数f7()把它的参数作为一个非const引用(reference)(C++中另一种处理地址的办法,这是第11章讨论的主题)。从效果上讲,这与取一个非const指针一样,只是语法不同。在C++中不能编译通过的原因是会产生一个临时量。

8.3.2.1 临时量

有时候,在求表达式值期间,编译器必须创建临时对象(temporary object)。像其他任何对象一样,它们需要存储空间,并且必须能够构造和销毁。区别是从来看不到它们—编译器负责决定它们的去留以及它们存在的细节。但是关于临时量有这样一种情况:它们自动地成为常量。通常接触不到临时对象,改变临时量是错误的,因为这些信息应该是不可得的。编译器使所有的临时量自动地成为const,这样当程序员犯那样的错误时,会向他发出错误警告。

在上面的例子中,f5()返回一个非const X对象,但是在表达式:

8.3.2 返回const值 - 图5

中,编译器必须产生一个临时对象来保存f5()的返回值,使得它能传递给f7()。如果f7()的参数是按值传递的话,它能很好地工作,然后在f7()中形成那个临时量的副本,不会对临时对象X产生任何影响。但是,如果f7()的参数是按引用传递的,这意味着它取临时对象X的地址,因为f7()所带的参数不是按const引用传递的,所以它允许对临时对象X进行修改。但是编译器知道:一旦表达式计算结束,该临时对象也会不复存在,因此,对临时对象X所作的任何修改也将丢失。由于把所有的临时对象自动设为const,这种情况导致编译期间错误,因此这种错误不难发现。

然而,下面的表达式是合法的:

8.3.2 返回const值 - 图6

尽管它们可以编译通过,但实际上存在问题。f5()返回一个X对象,而且对编译器来说,要满足上面的表达式,它必须创建临时对象来保存返回值。于是,在这两个表达式中,临时对象也被修改,表达式被编译过之后,临时对象也将被清除。结果,丢失了所有的修改,从而代码可能存在问题—但是编译器不会有任何提示信息。对于用户来说,像这样的表达式很简单,他可以找出问题所在,但是,当事情变得复杂后,就可能在这方面出差错。

类对象常量是怎样保存起来的,将在本章的后面介绍。