C语言中一般使用#define来定义常量,而C++中推荐使用const。因为#define定义的常量只是定义了名字和值的映射,存储在符号表中,在预处理阶段进行名值替换,不会进行类型检查操作。而const会进行类型检查。
c++中const默认是内部链接的,也就说,const仅在const被定义过的文件里才是可见的,而在链接中不能被其他编译单元看到。这是因为const默认也是放到符号表中的,不会为其分配内存空间。但也有例外:当使用extern和取const变量的地址时,会强制为其分配内存空间。e.g. extern const int size=10; 或 const int size=10; const int * p=&size。都会分配空间。
如果在一个文件中如此定义:extern const int size=10;而另一个文件中有:extern const int size;那么第一个是定义,而第二个是声明。它们指的是同一个size。也就是说,extern使const变为了外部链接。
const的使用已经渗透到了C++的很多方面:
const int a[]={1,2,3};这里必须初始化。const int a[3];是错误的。
const int * p;这里不需要初始化。自己想想为什么?另一种形式:int const * p;
int a; int * const p=&a;这里必须初始化。
const int* const p=&a;
此外字符串是常量的。所以对一个字符串的修改会出错。
char* p="freewater";
*p='a';//error!
不过我们可以使用字符数组:
char str[]="freewater";
*str='a';//ok
函数参数和返回值
返回const值
const 修饰函数参数表明函数中该参数是不可变的。而修饰返回值,对对于内部类型来说,按值返回的是否是一个const,是无关紧要的,因为返回值本身就是一个变量的副本,所以按值返回一个内部类型时,应该去掉const。当处理用户定义的类型时,按值返回常量是很重要的,表明该函数的返回值不能作为赋值号=的左值。
临时变量
有时候,在表达式求值过程中必须创建临时变量。临时变量自动成为常量。编译器使得所有的临时变量自动成为const。
类里的const
在一个类里放一个const值意味着:在这个对象的生命周期内,它是一个常量。然而,对这个变量来讲,每个不同的对象可以含有一个不同的值。且这个const值必须通过构造函数初始化列表初始化。因为这个常量会被分配空间,因此不能为静态分配数组时使用的长度。
但是如果使用static const就可以生成一个编译期间的常量。e.g.
class T{
static const int size=100;
int a[size];//ok
};
旧版本的c++中,不支持在类中使用static const。可以使用enum替换。
class T{
enum {size=1000};
int a[size];//ok
};
const 成员函数
如果声明一个成员函数为const,则等于告诉编译器该成员函数可以为一个const对象所调用。一个没有被明确声明为const的成员函数被看成是将要修改对象中数据成员的函数,而且编译器不允许它为一个const对象所调用。也就说,const对象既可以调用const成员函数,也可以调用非const成员函数。而非const对象只能调用非const成员函数。
仅仅声明一个函数在类定义里是const是不够的,编译器强迫程序员在定义函数时要重申const的说明。(const已成为函数标示符中的一部分,所以编译器和连接器都要检查const。)
const成员函数是不能对成员变量进行修改的。但是仍然可以通过强制类型转换来突破这种限制。
class Y{ int i; public: Y(); void f() const; }; Y::Y(){i=0;} void Y::f()const{ //i++;//error ((Y*)this)->i++;//ok,but not recommend const_cast<Y*>(this)->i++;//recommend
此外,C++中特别提供一个关键字mutable,来支持在const成员函数中修改成员变量。只需使用mutable来定义
成员变量即可。
class Y{ mutable int i; public: Y(); void f() const; }; Y::Y(){i=0;} void Y::f()const{ i++;//ok and simple }