分类: C/C++
2011-02-24 23:47:22
const最初动机是取代预处理器#defines进行值替代。
1,值替代
为所有的内部数据类型以及由它们所定义的变量使用限定符const
2,头文件里的const
const默认为内部连接。当定义一个常量时,必须赋一个值给它。C++编译器通常并不为const分配存储空间,相反它把这个定义保存在符号表里,当const被使用时,在编译器会进行常量折叠。
3,const的安全性
const int i=100;//typical constant
long address = (long)&i;//forces storage
**迫使编译器给i分配存储空间
4,集合
编译器不能把一个集合存放在它的符号表里,所以必须分配内存。
const int i[]={1,2,3,4};
float f[i[3]];//error C2057: 应输入常量表达式
**i[3]在编译时不能被使用,编译器在编译期不需要知道存储的内容。
5,与C语言的区别
C规范包含const,意思是“一个不能改变的普通变量”。总是占用存储而且它的名字是全局符。C编译器不能把const看成一个编译期间的常量。
const bufsize=100;
char buf[bufsize];//bufsize占用存储,C编译器不知道在编译期的值。
C语言可以这样写:
const bufsize;//C++中编译错
extern const bufsize;//declaration only,C++中未指定值时只能声明const变量
6,指针
处理const指针时,编译器努力阻止存储分配并进行常量折叠。
const int* x;//指针x,指向一个const int。这里不需要初始化,因为x可以指向任何东西,即x不是一个const
int const* x;//意义同上
int d=1;
int* const x=&d;//x是一个指针,指向int的const指针。const变量,编译器要求给它一个初始化值。这个值在指针生命期不变。
int * u=&w, v=0;//定义一个int* u和一个非指针int v
7,
int d=1;
const int e=2;
int* v=&e;//illegal,e const
int* v=(int*)&e;//legal but bad practice
C++有助于防止错误发生,但如果程序员自己打破这种安全机制,它也无能为力。
8,const没被调用的地方是串字面值
char* cp="howdy";
编译器接受它而不报告错误,从技术上讲,这是一个错误。
char* cp="howdy";
*cp=(const char)"new str";//0x00411a7d 处最可能的异常: 0xC0000005: 写入位置 0x00417880 时发生访问冲突
串字面值实际上是常量串,然而编译器把它们作为非常量看待,因为有许多现有的C代码是这样做的。
9,函数参数
如果以值传递对象时,对用户来说,用const限定没有意义(const意味着传递的参数在函数里不能被修改)。以const返回值或地址,意味着值或地址内容不会被改变。
void f1(const int i){
i++;/illegal --compile time error
}
为了不是调用者混淆,在函数内部用const限定参数优于在参数表里用const限定参数。
void f2(int ic){
const int& i=ic;
i++;//illegal compile time error
}
这时对象的常量性不是函数特性标志的部分,仅对函数实现由意义,所以它对用户来说是不可见的。
10,返回const值
const int g();
:::返回这个变量的值,因为这个变量被制作副本,所以初值不会被修改。这时const看起来没有什么意义。
int f3(){return 1;}
const int f4(){return 1;}
int main(){
const int j=f3();//works fine
int k=f4();//but this work fine too
}
:::对于内部数据类型,返回值是否是常量并没有关系,所以返回内部数据类型,最好去掉const避免用户程序员混淆。返回内部数据类型值时,const没有意义原因是:编译器已经不让它成为一个左值,因为它总是一个值而不是一个变量。
处理用户定义类型时,返回值为常量是很重要的,如果返回一个常量类对象,则它不能是一个左值。
11,临时变量
有时在求表达式值期间,编译器必须建立临时对象。编译器负责决定它们的去留以及它们存在细节。它们自动成为常量。
12,传递和返回地址
无论什么时候传递一个地址给函数,都应有尽可能用const修饰。是否选择返回一个指向const的指针,取决于我们想让用户用它干什么。
const int* const w(){
static int i;
return &i;
}
int* ip2=w();//Not ok 编译器拒绝把函数w()的返回值赋给一个非const指针,ip2指向的值可以被改变
const int* const ccip=w();//ok 指针ccip指向的值不可改变,指针不可改变
const int* cip2=w();//ok 指针cip2指向的值不可改变,w()返回的值被拷贝赋值给cip2
13,标准参数传递
通过const引用传递参数,函数将不改变该地址所指的内容,从用户程序员的观点,效果恰好与值传递一样。
class X{};
X f(){return X();}//return by value
void g1(X&){}//pass by non-const reference
void g2(const X&){}/pass by const reference
g1(f1());//Error:const temporary created by f()
g2(f1());//g2 take a const reference
:::一个总是常量的临时变量,它地址可以传递给一个函数,临时变量通过引用传递给一个函数时,这个函数参数一定是const引用。
14,类里的const
目的:在一个类里建立一个局部常量,用户常数表达式,在编译期被求值。const无法满足,只能用enum完成。
类对象里const分配存储并代表一个值,这个值一旦被初始化就不能改变。类里使用const意思是:这个对象生命期内,这是一个常量。然而对不同的对象可以含一个不同的值。
在一个类里建立一个const时,不能给它初值。在构造函数主体里,必须初始化。但无法防止在构造函数主体的不同地方改变const的值。
构造函数初始化表达式
初始化表的初始化发生在构造函数的任何代码执行之前。
fred::fred():size(100){}
内部数据类型构造函数
把内部数据类型的构造函数(仅指赋值)扩展为一般情形,可以写:float pi(3.1415);
15,编译期间类里的常量
在类对象里进行存储空间分配,编译器不能知道const的内容是什么,所以不能把它用作编译期间常量。
class bob{
const int size=100;//error C2864: “X::size”: 只有静态常量整型数据成员才可以在类中初始化
int array[size];//illegal
enum { size=1000};//使用一个不带实例的无标记的enum,枚举的所有值必须在编译期建立。
int i[size];
:::使用enum不会占用对象的存储空间,枚举常量在编译期被全部求值。
static const也可以在类里定义静态常量
16,const对象和成员函数
仅仅声明一个函数在类定义里是const的,不能保证成员函数也如此定义,所以编译器迫使程序员在定义函数时要重申const说明。
const放在函数声明前意味着返回值是常量,这不合语法。必须把const标识符放在参数表后。
关键字const必须同样的方式重复出现在定义里,否则编译器把它看成一个不同的函数。
任何不修改成员数据的函数应该声明为const函数,这样它可以由const对象使用。
17,按位和按成员const
按位const意思是对象中的每个位是固定的,所以对象的每个位映像从不改变。按成员const意思是,虽然对象从概念上讲是不变的,但是某个成员可能有变化。const成员函数里改变数据成员的两种方法:
a,强制转换const,取this(这是当前对象的const指针)强制转换成指向当前对象的普通指针。
class Y{
int i,j;
public:
Y(){i=j=0;}
void f() const;
};
void Y::f() const{
i++;//Error --const number function
((Y*)this)->j++;//ok: cast away const-ness this是const指针,转换成普通指针
}
main(){
const Y yy;
yy.f();//实际修改了对象Y的成员
}
b,使用关键字mutable,指定一个特定数据成员可以在一个const对象里改变
class Y{
int i;
mutable int j;
public:
Y(){i=j=0;}
void f() const;
};
void Y::f() const{
i++;/error --const member function
j++;//ok mutable
}
18,可变的volatile
语法与const一样,volatile意思是“在编译器认识的范围内,这个数据可以被改变”。
const volatile对象,这个对象不能被程序员改变,但可通过外面的工具改变。
就像const一样,可以对数据成员、成员函数和对象本身使用volatile,可以并且也只能为volatile对象调用volatile成员函数。
关键字const能将对象、函数参数、返回值及成员函数定义为常量,并能消除预处理器defines的值替代。
其它关于const的文章:
1,C++面试之const、#define http://blog.csdn.net/crescent_star/archive/2009/03/18/4001602.aspx