C++ const关键字解析

文章内容为研读C++ Primer所作笔记,结合对网络上一些其他博客的理解整理而成。编译环境为Win10、VS2015。

本文部分思路与代码参考自C++ Primer章节2.4的内容。

 

const初始化

const关键字用来限定被修饰变量的值,使其不能被改变。有意思的是,一开始我以为仅会在编译时为编译器提供检查作用的const,原来也可以在运行时对变量进行限定,它的初始化形式可以是常量,也可以是一个返回常量的函数,如下:

// e.g.const简单初始化
const int i = get_size();	// 正确:运行时初始化
const int j = 42;		// 正确:编译时初始化
const int k;			// 错误:k未经初始化的常量

编译时初始化const修饰的变量,在编译过程中会把当前文件中用到这个变量的地方都修改为对应的值(类似宏定义的作用)。由于C++编译器存在“分离式编译”机制(书p41),在文件被独立编译的过程中,不同文件下同名const变量实际上可以代表着各自独自的常量。如果希望共享一个文件中定义好的const变量,那么就需要为在其他文件当中的同名const变量加上extern修饰符作为外部引用常量。

// e.g.使在多文件下const定义的变量为同一个对象
// file_a.h
extern const int ext_var;    // 外部声明常量
void fn() {
	std::cout << ext_var << std::endl;
}

// file_b.h
extern const int ext_var = 4;    // 外部声明常量
// 经过试验,这里写成const int ext_var = 4;也编译通过且正常得到结果
// 但是显而易见地,如果在上面的file_a.h中的声明如果没有extern,编译器是会报错的,因为const的值没有被初始化。

// main.cpp
#include "file_a.h"
#include "file_b.h"
int main() {
	fn();
	return 0;
}

// 编译通过,控制台输出4

const的引用

“对const的引用”一般称之为“常量引用”,这里就不罗嗦了,直接上例子吧,用文字解释容易绕晕。

// e.g.常量引用的值不可被改变
const int c_i = 1024;
const int &ref_1 = c_i;
ref_1 = 42;    // 错误:ref_1是对(const int)类型的常量(c_i)的引用,原const修饰的变量不可被改变,因此它的引用同理也不可被改变。
int &ref_2 = c_i;    // 错误:试图用一个非常量引用指向一个常量对象

例子想要说明的是对const类型的引用也具有常量的特征,不可被修改。

// e.g.常量引用的初始化
int i = 42;
const int &r_1 = i;    // 正确:r_1是一个常量引用,绑定的是一个 非常量对象
const int &r_2 = 42;    // 正确:r_2是一个常量引用,绑定的是一个 字面值
const int &r_3 = r_1 *2;    // 正确:r_3是一个常量引用,绑定的是一个一般表达式
int &r_4 = r1 *2;    // 错误:r_4是一个普通的非常量引用,不可以绑定到带常量引用的表达式

另外还有一个比较有趣的例子,其中涉及上一节的const初始化以及本节的const的引用的内容,例子出自51CTO王健伟老师的课。

// e.g.const初始化以及const的引用结合分析
const int var(7);    // var初始化为7
int &var2 = (int &)var;    // 将常量var强制类型转换为int引用类型
var2 = 18;            // 这时var2可以被修改了

cout << var << endl;	// 输出7
cout << var2 << endl;	// 输出18

// 这里会编译成功,不会报错,且调试过程中,变量var和变量var2的地址是相同的,但是最后输出cout却是不同的,为什么呢?

// 其原因正如C++ Primer所说“在编译过程中就把用到该变量(var)的地方都替换成了对应的值”,所以在运行时var直接就输出7了

 

当一个常量引用被绑定至另外一种类型上时,编译器会生成一个临时量(temporary)对象,使其符合逻辑。倘若被绑定的不是常量引用(const int &),而是(int &),这种情况下编译器对将其理解为非法,具体原因如下。

// e.g.被绑定到另一类型的常量引用和引用
// 1.常量引用绑定
double d_val_1 = 3.14;
const int &ref_i_1 = d_val_1;    // 编译通过
// 等价于
// double d_val_1 = 3.14;
// const int temp_1 = d_val_1;    // 发生类型转换,双精度浮点数生成一个临时的整型常量
// const int &ref_i_1 = temp_1;    // 常量引用绑定到临时的整型常量上,这时候,整型常量实际上类似一个字面值,常量引用之所以可以绑定在它身上的原因在于,字面值常量有它在程序中存在的意义,因此语句合法。

// 2.引用绑定
double d_val_2 = 3.14;
int &ref_i_2 = d_val_2;    // 编译出错【无法用 "double" 类型的值初始化 "int &" 类型的引用(非常量限定)】
// 等价于
// double d_val_2 = 3.14;
// int temp_2 = d_val_2;    // 发生类型转换,双精度浮点数生成一个临时的整型变量
// int &ref_i_2 = temp_2;    // 引用绑定到临时的整型变量上,即ref_i_2引用temp_2。而事实上temp_2是否变化我们根本就不关心,引用绑定它没有任何的意义,因此C++将此当作为非法语句。

常量引用可能引用一个并非const的对象,因为它仅仅对引用可参与的操作做出了限定,对于引用的对象本事是否为一个常量却没有限定。若引用的对象本身是个非常量,就会有方法改变它的值。

// e.g.受常量引用绑定的变量对象仍可以被修改
int i = 42;    // 变量初始化
int &r_1 = i;    // 非常量引用
const int &r_2 = i;    // 常量引用
i = 0;    // 正确:变量值可以被改变
r_1 = 1;    // 正确:非常量引用可以修改绑定的变量对象的值
r_2 = 2;    // 错误:常量引用不被允许修改绑定变量对象的值

指针和const

 

 

TODO:(p56~p60)

  1. 指针和const
  2. 顶层const
  3. constexpr和常量表达式
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值