C++面向对象程序设计
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

2.5 const常量

2.5.1 常量的定义

变量实质上是在程序运行过程中其值可以改变的内存单元的名字。常量就是在程序执行过程中其值固定不变的内存单元的名字。在C++中,常用const修饰符定义常量,方法如下:

const 常量类型 常量名=常量值;

例如:

const i=10;           //L1
const int i=10;          //L2
const char c='A';         //L3
const char s[]="C++ const !"; //L4

语句L1和L2等价,C++将没有指定类型的常量定义默认为int类型(在Visual C++ .NET中不允许默认值,L1是错误的),L3定义了字符常量c,L4定义了字符常量数组s。

说明:① const常量必须在定义时初始化,且常量一经定义就不能修改(常量名不能出现在赋值符“=”的左边),例如:

const int n;          //错误,常量n未被初始化
const int i = 5;         //定义常量i
i = 10;            //错误,修改常量
i++;             //错误,修改常量

② 在C++中,表达式可以出现在常量定义语句中。如果定义的常量是整型,则类型关键字int可以省略。例如:

int j,k=9;           //L1
const i1=10+k+6;        //L2
const int i1=10+k+6;      //L3
const i2=j+10;         //L4

当常量定义语句中出现表达式时,C++将首先计算表达式的值,然后把计算结果指定给常量。语句L2省略了类型关键字int,它与L3是完全等价的,相当于“const int i1=25;”。语句L4尽管在有的编译环境下不会出错误,但没有意义,因为j的取值是未知的。

③ 在C++中,仍然可以采用#define定义常量,但#define采用宏代换方式进行常量的处理,不具有类型检查机制,存在不安全性。

2.5.2 const与指针

const可以与指针结合,由于指针涉及“指针本身和指针所指的对象”,因此它与常量的结合也比较复杂,可分为三种情况,如图2-1所示。

图2-1 const与指针的三种关系

图2-1中的type代表C++中的任意数据类型,p是一个指针变量。其中,第一种是常量指针,即指针是常量,不能被修改,但其所指内存区域是变量,可修改;第二种是指向常量的指针,即指针是变量,可再指向其他内存单元,但其所指单元是常量,不能修改;第三种是指向常量的常指针,指针及其所指内存单元都为常量,皆不能被修改。

例2-7】 const与指针的关系。

//Eg2-7.cpp
#include <iostream.h>
int main(){
    char *const p0;    //L1  错误,p0是常量,必须初始化
    char *const p1="dukang";   //L2  正确
    char const *p2;    //L3  正确
    const char  *p3="dukang";   //L4  正确
    const char *const p4="dukang"; //L5  正确
    const char *const p5;       //L6  错误,p5是常量,必须初始化
    p1="wankang";   //L7  错误,p1是常量,不可改
    p2="wankang";   //L8  正确,p2是变量,可改
    p3="wankang";   //L9  正确,p3是变量,可改
    p4="wankang";   //L10 错误,p4是常量,不可改
    p1[0]='w';    //L11 正确
    p2[0]='w';    //L12 错误,*p2是常量,不可改
    p3[0]='w';    //L13 错误,*p3是常量,不可改
    p4[0]='w';    //L14 错误,*p4是常量,不可改
   return 0;
}

例中,*p2和*p3的定义形式等价,只是p2没有初始化,p3被初始化了。请结合图2-1和本例中的注释理解各语句正确和错误的原因。

const对指针和变量之间的相互赋值具有一定影响:const对象的地址只能赋给指向const对象的指针,指向const对象的指针可以被赋予一个非const对象的地址,否则引起编译错误。例如:

int x=9;     //L1
const int y=9;    //L2
int *p1;       //L3
const int *p2;     //L4
p1=&y;       //L5,错误
p2=&x;        //L6,正确

语句L5将引起编译错误,若改为“p2=&y;”则是正确的,请结合上述说法理解其中原因。

2.5.3 const与引用

在定义引用时,可以用const进行限制,使它成为不允许被修改的常量引用。例如:

int i=9,
int &rr=i;
const int &ir=i;
rr=8;
ir=7;         //错误

最后一条语句是错误的,因为ir是const引用,不允许通过它修改对应的变量i。

const引用可以用常量初始化,但非const引用不能用常量初始化。例如:

int i=2;          //L1
const double &ff=10.0;   //L2
const int &ir=i+10;    //L3
int &ii=3;         //L4,错误

语句L4错误,语句L2、L3正确的原因与编译器的处理方式有关。编译器在实现常量引用时生成了一个临时对象,然后让引用指向这个对象。但该对象对用户而言,是隐藏不可知的,不能访问。例如,对于

const double &ff=10.0;

编译器将其转换为类似于下面的形式:

double temp=10.0;
const double &ff=temp;

编译器首先产生不为用户所知的临时变量temp,然后将引用指向它。temp将保持到引用的生命期结束。