const限定符全接触
Oct 5th, 2007 by sha
这段时间一直在复习准备笔试,好久都没有来ksarea了。今天上来一看,心中感动不已,原来king一直都在不断的更新我们的空间……哎,自己要好好的反省一下了,呵呵。以前一直都没有好好的学习C++里面的一些细节知识点,今天看了看const限定符,所以也在这里写几句,算是对ksarea一点小小的弥补吧 ![]()
const限定符把一个对象转换成一个常量,如 const int bufSize=512; 申明bufSize的时候,如果不将它初始化,那么编译器会报错:const object must be initialized if not extern。因为bufSize被const限定符限制,如果不在申明bufSize的时候给它赋值,就不能再给它赋值(因为常量在定义之后就不能被修改),它就没有任何意义了。
简单的介绍了一下const的含义之后,下面将详细的讨论一下const变量的作用域、const指针、指向const对象的指针、const引用的相关用法。
1.const变量作用域
在全局作用域声明的普通全局变量在整个程序中都可以被访问,如:
int counter;
//file_2.cpp
extern int counter; // use counter from file_1.cpp
++counter; // increments counter defined in file_1.cpp
但是在全局作用域声明的const变量却是定义该对象文件的局部变量,此变量只存在与这个文件中,不能被其他文件访问,有些类似于使用static修饰的全局变量。但是如果在其前面再加上extern,那么就意味着可以在其他文件中共享。如:
extern const int counter=100;
//file_2.cpp
extern const int counter; // use counter from file_1.cpp
++counter; // increments counter defined in file_1.cpp
(const变量在默认的情况下是定义该变量文件的局部变量,之所以要这样定于语法,是因为允许const变量出现在头文件中,当一个头文件中定义了一个const变量,而它又是局部变量,那么在任何一个包含这个头文件的源文件都会有属于自己的const变量。而一般的非const变量则被默认为extern)
2.const指针
与任何const变量一样,const指针必须在定义的时候初始化。const指针即被const限定符修饰的指针,意味着它只能指向初始化时所指向的值,初始化之后就不能改变它的指向。如:
int a=20;
int *const p=num; // ok
p=a; // errer
const指针 只能限制指针本身是const类型,不能限制它所指向对象的值是否被改变,也不能限制通过这个指针改变它所指向对象的值。当然,当const指针在初始化时就指向了const类型的对象,那么它所指向对象的值也就不能被改变了,但是要十分明确的是对象是否可以被改变与const指针无关,只和指针所指向的对象的类型有关。
3.指向const对象的指针
直接先看例子吧:
const double *pd;
pd=&p ;
*pd=4.13; // errer
指向const对象的指针 表示了一种特殊的限制:不能通过这个指针改变所指对象的值。看清楚哦,仅仅是不能通过指针改变其对象,绝对不代表其对象不能被改变,也不代表这个指针不能指向别的对象。“其对象能否被改变”还是和2中的最后做出的解释一样,得由其对象自己的类型所决定;而“这个指针能不能指向别的对象”也是由这个指针的类型所决定(如果象2中那样定义这个指针,那么就成了const指针,也就不能指向别的对象了)。
虽然我们不能通过这个指针改变所指的对象的值,但是可以通过别的指针来改变,如:(接着上面的例子)
pd=&p;
*p=4.13; // ok
这个时候虽然pd还是指向的p,但是p的值已经由原来的3.14变成4.13了。
看到这里,相信读者应该很明白了吧。const对象、const指针、指向const对象的指针,这三者是相互不影响、不限制的关系,可以任意搭配。在这里举个将三者都搭配起来的例子:
const char *const pc=&ch;
看到这个例子后,是不是感觉心里终于踏实了?呵呵,谁都不能被改变了。
最后再给大家提一个问题,检查一下自己是不是真正理解了:
const ps cps;
请问cps变量是什么类型?(先想一想再往下看吧 ^_^)
答案:cps是const指针!上面的代码等价于:string *const cps;
呵呵,你答对没有呢? 据说没有见过这个例子或者不了解此知识点的人,99%都会答错,剩下的1%是猜对的。
hoho,再次受伤……
其实是这样的:我们在定义普通的const对象的时候,合理的定义应该是:string const s=”abc”;const在类型的后面,从右向左阅读该声明语句会发现const直接修饰的是“s变量”,例如:
double * const ch=&p; //const修饰的是“ch变量”
const double *pt=&p; //const修饰的是“pt指针指向pi”
但是很多人阅读c++程序的时候,习惯将const类型放在前面,所以为了方面起见,规定将const放在类型前面或者后面都是符合语法的(不过我个人不太赞同这种折中的做法,容易误导人,呵呵)。所以呢……我们回到刚才提出的问题:
const ps cps; 等价于: ps const cps; 等价于: string * const cps; 即:cps是const指针。 ^_^
有关这个知识点,最后做两点说明:
1).const位置问题:
char const ch=’a'; 等价于: const char ch=’a';
但是 char *const ch=’a'; 不等价于: const char * ch=’a';
2).const变量赋值问题:
非const指针不能指向const对象,如:
const double p1=p// ok
double *p2=&p//errer!! cannot convert from 'const double *' to 'double *'
double *const p3=&p //errer!! cannot convert from 'const double *' to 'double *const'
4.const引用
当一个子函数需要使用主函数中某个变量的值时,我们可以用const引用的方式传递给子函数。用这种的好处在于:1)不用再为子函数分配内存来保存主函数中这个变量的副本(因为引用表示使用别名,而不是使用副本);2)防止子函数中无意识的改变了此变量的值。我们举例子说明,如:
{const int &b=a;}
void main()
{
int i=2;
const int &j=i;
func(i);
}
//另外说明一下,作为引用的特点:调试程序跟踪到内存中,可以发现程序里&a;&b;&j的值是一样的
以上就是本人了解的有关const的相关知识,希望对你有所帮助,对于其中的错误或是不完整之处,欢迎跟帖。
added by king as follows:
看了sha写的上述内容,对const有了一个全面的理解,下面将和sha讨论的最终一些结果总结如下:
1)明确const Type variable-name和Type const variable-name(更加符合标准)是一回事,都表示申明一个const对象,也就是说,这个变量在后续程序中是不能被修改的。这里的Type可以是char、int、long等等。
而对于指针变量或者变量的引用则有所不同:
const int * pi1=&i;
int const * pi2=&i;//和上述申明一回事,都是申明一个const对象指针
const int & ri1=i;
int const & ri2=i;//和上述引用一样,不能通过这个引用修改i的值
//也就是说,这里不是把int* 和int&看作是一种类型,我们只能说,他们分别是int型指针,int型引用。
2)上文中举了如下例子:
const ps cps;
其中将string*视为一种类型,因为typedef的作用就是定义类型的同义词。
所以这里将string*看作是一种类型(和上述第1条中说明有差别),ps看作是这种类型的别名,所以这里的const ps cps 标准化为ps const cps,所以它应该是等效于string * const cps(const指针)。
如果这里的typedef string * ps;换成#define ps string*,情况又会怎么样呢?答案是那条语句最终等效於const string* cps。
why?因为typedef不是简单的文本扩展,而是相当于定义一种新的类型,而define却仅仅是简单的文本扩展,所以编译器不会把ps看作是一种类型,那么也就不会把const ps cps视为ps const cps了,所以最终只是进行简单的文本替换为const string* cps。
3)看看有关const的代码块:
str[1]='a';//error!!
//const char* str="lovesha";//第一条语句等效於这条,推荐使用这种写法,避免难于发现的错误。
const int * pi1=&i;//right!只是不能通过pi1指针来修改&i这个内存的值
const int *ppi1=pi1;//right!但是int *ppi1=pi1;这样就是错误的,因为不能通过pi1修改对象值,这样定义却导致可以使用ppi1来改变对象的值
int * const pi2=&i;//right!只是不能修改pi2的值而已
//pi2++;//error!
int yy=xx;//right!其实yy是xx的一个副本,其中的值为1
const int &p2=c;//p2是c的别名,但是不能通过这个别名来修改c的值
// int const &p2=c; //right!和上条语句的定义功能一致。
//int & const p2=c;//error!因为int&不是一种类型,所以这种申明方式是错误的。
const int & p3=p2;//
// int & p3=p2; //error!!本来p2值是不能改变的,这个语句却可以让p3可以改变p2的值
总之,以上代码可以总结为一句话:权限是允许缩小,但是不允许私自扩大。例如一个变量是const的(理解为权限小),不能让一个指向非const对象指针(理解为权限大)来指向它的;而一个非const变量(权限大)是可以让一个指向const对象的指针(权限小)来指向它的。
added again by sha as follows:
“用常量表达式初始化的const对象是一个常量表达式!”
常量表达式(大于等于1)可以作为用来表示数组得到维数,如:
char p[i]={'a','b','c'};
cout<<p<<endl;
const int j=i;
char p2[j]={'a','b','c'};
cout<<p2<<endl;
以上都是输出的abc。再补充一点,数组的维数必须用大于等于1的常量表达式定义,此常量表达式只能包含整形字面值常量、枚举常量或者用常量表达式初始化的整形const对象,非const对象以及要到运行时阶段才知道其值的cosnt变量都不能用于定义数组的维数。一下分别举例说明:
char p[a];//error!
const int a1=20;
char p1[a1];//ok
char p3[a1+1];
int init(){int z=2; return z;}
const int init2(){int z=2; return z;}
const int a4=init ();// ok: a4=2
char p4[a4];//error!! a4 is unkown until run time
char p4[init()];//error!! non-const expression
char p4[init2()];//error!! init2()is unkown until run time
宣传力度不够啊!这么好的文章怎么没人看啊!
我来踩踩!!
记得回访哦
http://home.51.com/home.php?user=lukuibiao