我看的书是《c++ primer 第五版》,在书的第二章第四节(p53)专门讲了const限定符相关知识,学习之后在此做一个总结:
const限定谁,谁就是只读而不能被改变的 。这是我看了书上相关细小的知识点后总结的一句,然后再根据书上的内容去解读和验证一下。
const int bufSize=512;//输入缓冲区大小
一个变量代表着一个内存空间,变量名是这块内存空间的标号,const就像一把锁,把这块空间锁了起来。这样,可以形象地把内存空间比喻成一个大的柜子,操作系统就是这些柜子的管理员,那么像
int bufSize;
这样就是声明说我要一个int 大的尺寸柜子,给柜子贴的标号是bufSize,但是你并没有往柜子里装自己的东西,管理员记住了你的需求,但是没有实际给你分配柜子,而像下面
int bufSize=512;
这样,就是你向管理员要了int 大的尺寸柜子,给柜子贴的标号是bufSize,并且要把自己的东西装到柜子里,这个时候管理员就必须真的为你分配一个柜子了,而柜子里的东西,对上标号就可以存取了。接着
const int bufSize=512;
这个就是你自己带了一把锁,向管理员要了int大的尺寸的柜子,给柜子贴的标号是bufSize,把自己的东西装进去,并且给柜子上了锁,至此,柜子里的东西就被锁死了,企图通过标号等任何手段改变,都是不被允许的。(想要改变就只能改源代码,这也保证数据是安全的,另外,记住:这个柜门的玻璃是透明的)
而像下面这样:
const int bufSize;
不放自己的东西,还锁柜子,如果说是忘记了,管理员提醒一下你,如果故意的,管理员岂容尔等胡作非为。
——const对象必须初始化
反正给了你要的空间,至于放什么样的东西,管理员是不管的,只要在锁上前放进去就可以了。
——初始值可以是任意复杂的表达式
const int i=get_size();//正确,运行时候初始化 const int j=42;//正确,编译时初始化
初始化和const
int i=42; const int ci=i;//正确,i的值被拷贝给了ci。柜子里装的东西是什么样子,从哪来的都可以 int j=ci;//正确,ci的值被拷贝给了j。柜子是透明的,你完全可以照抄一份,这也没有改变柜子里的东西
默认状态下,const对象仅在文件内有效
当以编译时初始化的方式定义一个const的对象时,例如前面的 cosnt int j=42; 程序在编译的时候,把遇到该变量的地方,都替换成42,但是如果程序是由多个文件组成的,要完成这种替换,每个文件中都要有j的定义,默认情况下,如果每个文件中都定义一个同名的const对象,则每一个const对象都是相互独立的。
如果我们想让不同文件能共享同一个const对象,就是说,只在一个文件中定义,可以在其他文件中使用,解决办法就是,在定义和声明的时候都添加extern;
//file_1.cpp 定义并初始化了一个常量,该常量可以被其他文件访问 extern const int bufSize=fcn(); //file_1.h 头文件 extern const int bufSize;//与file_1.cpp中定义的bufSize是同一个
const 的引用
const int ci=1024;//标号为ci的柜子里锁住一个1024,无论用什么手段都不能更改 const int &r1=ci;//正确,引用及其对应的对象都是常量 给柜子又开了一个门,贴了一个r1的标号,但是必须要求这个门也是上锁的 r1=42;//错误:r1是对常量的引用 不能进入锁着的门内修改东西 int &r2=ci;//错误:试图让一个非常量引用指向一个常量对象 想新开一个不上锁的门,管理员是不会允许的
初始化和对const的引用
int i=42; const int &r1=i;//允许将const int&绑定到一个普通的int对象上 const int &r2=42;//正确,r2是个常量引用 const int &r3=r1*2;//正确,r3是个常量引用 int &r4=r1*2;//错误,r4是个普通的非常量引用
这段怎么理解呢?第二条语句 const int &r1=i; 标号为i的柜子原本是一个没有上锁的普通柜子,现在在这个柜子侧边新开了一扇标号为r1的门,又给这扇门上了锁,但是,柜子里的东西还是可以通过标号为i的门更改的,但是无论如何都不能通过标号为r1的门去更改柜子里的东西。第三条语句 const int &r2=42;42是字面量,是没有地址的,这时候管理员就出面开了个临时的未贴标号的柜子,再把42装进去,并且上了锁,此时柜子里的东西就无法更改了,然后在柜子侧边再开一扇标号为r2的门,此时上锁就是必须的,否则柜子里的东西就有可能通过标号r2被更改。第三条语句 const int &r3=r1*2;在标号为r1的门那里copy一份数据,然后*2得到一个常量值,后面的操作就与三条语句一样了,给标号为r3的门加锁是必须的;第四条语句int &r4=r1*2;相比第三条语句,区别是没有加锁,柜子里的东西可能通过这扇门更改,管理员不允许这样的行为。
这里还是很有必要把书中原文截图到此:
对const的引用可能可能引用一个并非const的对象
给标号为i的柜子开一个标号为r2的门,然后把这扇门锁起来,柜子里的东西当然可以通过其他没锁的门更改,但是无论如何也不能通过标号为r2的门更改柜子里的东西。
指针和const
(这里要理解指针和引用的区别和联系,如果还用柜子理论的话,前面引用是在柜子上挖新的门,贴标号,而指针则是一把真实存在的钥匙,我们需要开哪个门就把钥匙配成什么样子,这更加灵活了,但却需要加倍小心,比如我们拿到的新钥匙,如果我们还没确定要用它打开哪扇门,那么最好要把它复位成打不开任何门的空白钥匙(指针赋初值NULL),这样这把钥匙就是可控的)
顶层const
指针本身是一个对象,它又可以指向另一个对象,所以指针本身是否是常量和它所指向的对象是否是常量是两个不同的问题,所以为区分这两种情况,将指针本身是常量称为顶层const,而将指针所指向的对象是常量称为底层const。
更一般的,顶层const可以表示任意的对象本身是常量,这一点对任意的数据类型都适用,而底层const则与指针和引用这样的符合类型相关,比较特殊的是,指针类型既可以是顶层const,也可以是底层const。
int i=0; int* const p1=&i;//不能改变p1,这是个顶层const (看const右边紧跟着的是什么,这里是p1那const就是限制p1的) const int ci=42;//不能改变ci,这是一个顶层const const int *p2=&ci;//p2的值可以改,但是不能改变p2所指向的内存空间的值,这是一个底层const
后面的初始化和copy时候的顶层const和底层const问题,记住const限定谁,谁就是只读而不能被改变的,任何企图通过别的方式来改变的行为都是错误的。这里别的方式指的是指针(配一把钥匙打开柜子)或者引用(给柜子再开一扇不上锁的门)。如果柜子里的东西是const的,那么这个柜子的所有钥匙仅有打开柜子向外展示的权限,所有在柜子上新开的门也仅提供向外展示的权限,超越权限就是非法的。