《C++ Primer Plus》中的const

const与指针

可以用两种不同的方式将const关键字用于指针。第一种方法是让指针指向一个常量对象,这样可以使用该指针来修改所指向的值,第二种方法是将指针本身声明为常量,这样可以防止改变指针指向的位置。

声明一个指向常量的指针pt:

int age = 39;
const int * pt = &age;

该声明指出,pt指向一个const int,因此不能使用pt来修改这个值,换句话来说,*pt的值为const,不能被修改。

  • 将常规变量的地址赋给常规指针 √
  • 将常规变量的地址赋给指向cosnt的指针 √
  • 将cosnt变量的地址赋给指向const的指针 √
  • 将const的地址赋给常规指针 ×

C++禁止将const的地址赋给非const指针。

如果数据类型本身并不是指针,则可以将const数据或非const数据的地址赋给指向const的指针,但只能将非const数据的地址赋给非const指针。

应尽可能使用const,将指针参数声明为指向常量数据的指针有两条理由:

  • 可以避免由于无意间修改数据而导致的编程错误
  • 使用cosnt使得函数能够处理cosnt和非const实参,否则只能接受非const数据

const引用参数

如果意图是让函数使用传递给它的信息,而不对这些信息进行修改 ,同时又想使用引用,则应使用常量引用,在函数原型和函数头中使用const:

double refcube(const double &ra);

如果这样做,当编译器发现代码修改了ra的值时,将生成错误消息。

非cosnt引用作为函数参数,与常规参数传递相比,限制更为严格。仅当引用使用const修饰时,C++才会生成临时变量,以解决实参与引用参数不匹配的问题。如果引用参数是const,则编译器将在下面两种情况下生成临时变量:

  • 实参的类型正确,但不是左值
  • 实参的类型不正确,但可以转换为正确的类型

左值参数是可被引用的数据对象,例如,变量、数组元素、结构成员、引用和解除引用的指针都是左值。非左值包括字面常量(用引号括起来的字符串除外,它们由其地址表示)和包含多项的表达式。常规变量属于可修改的左值,而const变量属于不可修改的左值(在C语言中,左值最初指可出现在赋值语句左边的实体)。

如果声明将引用指定为const,C++将在必要时生成临时变量。实际上,对于形参为const引用的C++函数,如果实参不匹配,则其行为类似于按值传递,为确保原始数据不被修改,将使用临时变量来存储值。总之,使用非const引用参数使得函数有修改实参的可能,但const引用消除了这种特性,使得引用实现了类似与按值传递的行为。

如果函数调用的参数不是左值或与相应的const引用参数的类型不匹配,则C++将创建类型正确的匿名变量,将函数调用的参数的值传递给该匿名变量,并让参数来引用该变量。

应尽可能使用const:

  • 使用const可以避免无意中修改数据的编程错误
  • 使用const使函数能够处理const和非const实参,否则只能接受非const数据
  • 使用const引用使函数能够正确生成并使用临时变量

const用于引用返回类型

对于原型为:

free_throws & accumulate(free_throws & target, const free_throws & source);

的函数,语句:

accumulate(dup, five) = four;

可以通过编译,这是因为在赋值语句中,左边必须是可修改的左值。也就是说,在赋值表达式中,左边的子表达式必须标识一个可修改的内存块。在这里,函数返回指向dup的引用,他确实标识的是一个这样的内存块,因此这条语句是合法的。

常规(非引用)返回类型是右值——不能通过地址访问的值。这种表达式可出现在赋值语句的右边,但不能出现在左边。其他右值包括字面值(如10.0)和表达式(如x+y)。为何常规函数返回值是右值呢?这是因为这种返回值位于临时内存单元中,运行到下一条语句时,它们可能不再存在。

总之,使用非const引用作为作为函数返回值,增加了返回值被修改的可能,这点与常规(非引用)作为返回值是不同的。

假设要使用引用返回值,但又不允许执行像给accumulate()赋值这样的操作,只需将返回类型声明为const引用:

const free_throws & accumulate(free_throw & target, const free_throws & source);

现在返回类型为const,是不可修改的左值,因此上面提到的赋值语句不合法。

以const free_throws &类型为形参的display()函数可以调用accumulate():

display(accumulate(team, two));

但下面的语句不合法,因为accumulate()的第一个形参不是const:

accumulate(accumulate(team, three), four);

需要注意的是,在函数中,切勿返回指向临时变量的引用,因为函数运行完毕临时变量将不再存在。

const成员函数

const Stock land = Stock("Kludgehorn Properties");
land.show();

编译器将拒绝第二行,这是因为show()的代码无法确保调用对象不被修改。可通过将函数参数声明为const引用或指向const的指针来解决此类问题,但此处存在语法问题,即show()方法没有参数,其所使用的对象是由方法调用隐式提供的。因此需要一种新的语法——保证函数不会修改调用对象:将const关键字放在函数括号的后面,即函数声明为:

void show() const;

函数定义为:

void stock::show() const;

以此种方式声明和定义的类函数被称为const成员函数,就像尽可能将const引用和指针用作函数形参一样,只要类方法不修改调用对象,就应将其声明为const。这种在函数括号后面使用const限定符的做法,实际上是将指向调用函数的this指针限定为const,亦即不能使用this来修改对象的值。

而在

const Stock & topval(const Stock & s) const

中,括号中的const表明,该函数不会修改被显式访问的对象(作为函数参数的对象),括号后的const表明,该函数不会修改被隐式访问的对象(调用函数的对象)。此外,由于函数返回值为两个const对象之一(或为作为函数参数的对象,或为调用函数的对象)的引用,因此返回类型也应为const引用。

对于重载加法,由于存储计算结果的变量为仅在重载加法运算符函数生命周期内的临时变量,因此返回值应为对象而非引用。若进一步将返回值声明为const,则可避免与force1+force2 = net(force1 + force2 == net的误写)类似的代码引发问题。

posted @ 2020-03-04 09:46  溪嘉嘉  阅读(199)  评论(0编辑  收藏  举报