C++学习 十一、const, mutable关键字

前言

本篇继续C++学习,const关键字。

const常量

C++中,const被用于向编译器指定不变值,也就是常量。

const修饰普通类型的变量

当const用于修饰普通类型的变量时,该变量值将不能被修改,也就是成为一个常量,否则将报错error: assignment of read-only variable

const int a = 1;
a = 2; // error!

即使获取常量的地址,使用指针也无法修改常量的值。示例如下:

int *pa = (int*) &a;
*pa = 2;
cout << "a=" << a << endl;
// print: a=1

可见,使用指针仍然无法修改常量的值。

这是由于编译器对于程序会进行优化,对于const常量将直接采用类似于#define的方式,将a替换为1,而使用指针将修改时,内存空间中a的地址处的确被修改成了2

volatile关键字

volatile关键字可以关闭编译器的优化,使得指针能够修改const常量的值:

volatile const int b = 1;
int* pb = (int*) &b;
*pb = 2;
cout << "b=" << b << endl;
// print: b=2

尽量不要使用volatile使得const常量能够被指针修改,因为这并不符合使用const常量的初衷。

常量指针与指针常量

常量指针与指针常量是const关键字的重要应用,也是容易混淆的概念。

常量指针

我们通常说int指针,double指针等,都是说某个类型的指针。常量指针也是这样,即常量的指针:

int a = 1;
const int* pa = &a;

上面的声明指出,指针pa指向一个const int类型。

注意,常量的指针,不是说指针指向的值是常量,而是说对于该指针而言,这个指向值是个常量。换句话说,常量指针指向的值是只读的。下面的内容将报assignment of read-only location '*pa'

*pa = 2; // error!

但直接修改a就没问题:

a = 2;

此外,const int*int const*没有区别,都是常量指针,对应的汇编指令如下图所示:
在这里插入图片描述

指针常量

所谓指针常量,就是说指针本身是个常量。换句话说,指针保存的地址是个常量,不能改变指向的位置,示例如下:

int b = 1;
int* const p = &b;

常量指针保存的地址可以修改,但指针常量的地址不能被修改,否则会报error: assignment of read-only variable 'pb'

pa = pb;
pb = pa; // error!

一句话辨析

简而言之,常量指针int const* p*p只读;int* const pp只读。

const变量与指针

带const关键字的变量和指针有下面这三种可能的搭配关系:

int a = 1;
const int b = 1;
const int c = 1;

int const* pa = &a;
int const* pb = &b;
int* pc = &c; //error

其中,pa就是上面提到的常量指针,*pa只读,但可以修改apb也是常量指针,但是其指向的b也是const常量,因此*pb, b都是只读的;

int* pc = &c这样,将const变量赋给常规指针的操作将报error: invalid conversion from 'const int*' to 'int*'。这在上面提到过,使用const变量即认为该变量不应当被改变,而通过常规指针取地址则是希望能够改变变量,这在编译器看来是矛盾的。如果非要这么做,需要通过强制类型转换把常规指针转为常量指针。

常量引用

类似常量指针,常量引用也不能修改被引用的变量:

int d = 1;
const int& rd = d;
d = 2;
rd = 2; // error!

可以直接修改变量d = 2;,但是通过引用修改变量rd = 2;error: assignment of read-only reference 'rd'。即,常量引用变量自身是只读的。

此外,常规引用中,引用类型必须与被引用的变量类型相同,否则将报error: cannot bind non-const lvalue reference of type 'double&' to an rvalue of type 'double'。但常量引用不受类型限制:

int e = 1;
double& re = e; // error!
const double& cre = e;

上面实例中,第二行常规引用报引用类型错误,而第三行正确。

注意:C++ Primer Plus中提到,只有在const引用,并且引用类型正确但不是左值,或引用类型不正确但可以类型转换时,将生成临时变量。不过在我的程序中,即使是非const引用变量&re来引用int变量e,而报错信息是rvalue of type 'double',也就是说e仍然被创建了一个double类型的临时变量,只不过不能被非const引用变量绑定。实操与书本出现了矛盾。

const与函数参数

前几篇中提到,函数传参有传值,传地址,传引用的方式。传值不会影响实参的值,而传地址,传引用都可能改变实参。

如果希望函数传参不会影响实参的内容,可以通过const使实参只读。

const指针参数

函数参数类型前添加const关键字表明,该参数是只读的,即使参数是指针或是引用。指针经常被用于数组操作:

void printArray(int const arr[], int n){
	for(int i=0; i<n; i++){
		std::cout << arr[i] << std::endl;
		// arr[i] = i; // error!
	}
}

上面的示例中,int const arr[]表明数组arr是只读的,因此arr[i]=i;将报assignment of read-only location '*(arr + ((sizetype)(((long unsigned int)i) * 4)))'。根本原因实际上就是常量指针指向的值是只读的。

const引用参数

const引用与const指针行为类似。引用经常被用于大型结构,对象的操作:

struct myStruct{
	int a;
	float b;
	double c;
};

void printStruct(const myStruct& st){
	std::cout << st.a << std::endl;
	std::cout << st.b << std::endl;
	std::cout << st.c << std::endl;
	// st.c = st.b; // error!
}

上面的示例中,const myStruct& st表示结构st是只读的,因此st.c = st.b;将报error: assignment of member 'myStruct::c' in read-only object。根本原因实际上就是常量引用变量自身是只读的。

另外,还是要重复以下常量引用参数与临时变量:

const引用参数在以下情况下,生成临时变量:

  1. 实参类型正确,但是右值;
  2. 实参类型不正确,但可以转换为正确的类型。

并绑定到形参上。顺序是:传递实参,生成临时变量,与常量引用形参绑定。

const返回类型

函数的返回类型也可以声明const。

返回值

对于int,double这些普通返回值类型,加不加const都一样。

int const func(int a){
	return a;
}

返回引用

对于返回引用的函数,添加const关键字使得返回的引用成为不能修改的左值:

int const& func(int& a){
	return a;
}

mutable关键字

对于const结构变量或者const对象,我们不能修改其成员的值。

如果希望能够修改const结构变量、对象中某个成员的值,可以使用关键字mutable,示例如下:

struct myStruct{
	int a;
	float b;
	mutable double c;
};

void main(){
	const myStruct t {1, 2, 3};
	t.c = 5;
	// t.b = 4; // error
}

mutable修饰的成员,即使结构变量被声明为const常量,该成员也能够被修改,而修改非mutable成员则会报error: assignment of member 'myStruct::b' in read-only object

后记

本篇比较详细的记录了C++中const关键字的使用方法。下篇将探讨C++的作用域问题。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值