c/c++ 学习总结(4)--const关键字

const这个关键字在c++被“重载”了很多次,写在不同地方表示不同的意思。需要分别来说明:

const 修饰变量

第一点:const修饰的变量一定要初始化,const变量的值在编译时就要被确定,放在“代码段”的“.rodata”中,如果在程序中显示的修改const变量,会报编译时错误,如果一些技巧来欺骗编译器来修改const变量,其行为是未定义的。例如:

int num = 90;               //定义非const变量
const int &r = num;          //使用const引用来指向num,此后就不能通过r来修改num了
const_cast<int &>(r) = 100;  //使用RTTI来去掉r的底层const,然后再来修改r,这样做是安全的因为num本来就是非const
cout<<num<<endl; 			//输出num为100

//但是
const int num = 90;          //定义const变量
const int &r = num;          //使用const引用来指向num,此后就不能通过r来修改num了
const_cast<int &>(r) = 100;  //使用RTTI来去掉r的底层const,然后再来修改r,这样做行为是未定义的
cout<<num<<endl; 	         //输出会发现num依然为90,c++承诺常量始终是常量

第二点:const变量的连接性是内部的。在头文件中定义一个非const变量(如 int num = 10;),由于头文件可能会被多个实现文件包含,这个“num”变量是一个全局变量,它是一个强符号,链接器会报重复定义,有趣的是被const修饰的变量却不会,原因是const变量的连接性是内部的,它的作用域和static一样会被限制在当前翻译单元中,这种连接性为内部的变量,在编译时就被初始化了,不需要等到运行时(const变量的初始化可以被推迟到运行时,后面介绍)。

底层const与顶层const##

const在修饰指针变量和引用时会有顶层底层之分,看代码

	int num = 90;
	int * const p = &num;   //此时的p是带有顶层const的,它表示p是一个常量,因此必须初始化
	int const * p = &num;   //此时的p是带有底层const的,它表示不能通过p来修改num,而不管num本身是否为常量。
	const int  * p = &num;   //这种写法等价于上面的

	int num = 90;
	int const & r = num;   //此时的p是带有底层const的,它表示不能通过p来修改num,而不管num本身是否为常量。
	const int  & r = num;   //这种写法等价于上面的
	int  & const r = num;   //错误,没有这种写法

	//关于为什么没有上述写法,我的理解是引用的本质是一个“自解引用的常量指针”,如
	int &r = a;         //等价于 int *const r = &a;  这也是为什么引用必须初始化的原因
	cout<<a<<endl;      //等价于 cout<<*a<<endl; 
	//当然上述对引用本质的理解是从编译器角度来说的,还可以从单纯的概念层面来说明问题,今后会写一篇我对引用理解的文章。

const与constexpr及常量表达式##

第一点:先说说什么是常量表达式:常量表达式是在编译时就能确定其值且在运行时不会改变的量,一个变量是不是常量表达式由其变量类型和初始值决定。

int num  = 1;//不是
const int num = 1;//是
const int num = size();//不是,const修饰的变量可以在编译时初始化,也可以推迟到运行时

第二点:c++11引入了constexpr,被constexpr修饰的变量或函数必须在编译时计算出来,看代码

constexpr int num = size();        //size()必须为constexpr函数
//constexpr函数是一个隐式inline函数,还要求该函数的参数和返回值只能是字面值,不能是虚函数,不能是递归函数

第三点:constexpr与指针

cosntexpr int * p = &num;        //constexpr是顶层的
int * const p = &num;            //等价于上面的

constexpr const int * p = &num;      
const int * const p = &num;        //等价于上面的
//注意:由于constexpr的变量是一个在编译时确定的常量表达式,因此num必须有固定的地址,如全局变量和static变量。

const与函数重载

顶层const不能构成重载的依据,但底层const可以构成重载的依据,例如

void show(int num);
void show(const int num);   //顶层const,这两个不是重载

void show(int * p);
void show(int *const p);   //顶层const,这两个不是重载

void show(int & r);  
void show(const int &r);   //底层const,是重载

void show(int *p);
void show(const int *p);  //底层const,是重载

//另外
void show();
void show()const;   //可以重载

在类中使用const

当我们在类中声明const成员变量时,变量一定要初始化。我们需要在类的构造函数的初始化列表中初始化,不能在函数体中初始化(因为函数体是在对变量赋值,而不是赋值)。还需要注意的是:由于通过构造函数可以把该变量初始化成不同的值,使得不同对象之间的值不同(这可能不是我们所想的),所以可以采用匿名enum类型定义常量。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值