C++ const的那些事

const的那些事

1.顶层const(top-level const ) 与底层(low-level const )

指针本身是一个对象,因为,指针实际对应着内存单元的一段存储空间,然而,指针所指向的也是一个数据对象,因此,指针是一个常量与指针所指向的是一个常量是两个完全不同的概念, 顶层 const 表示的是 指针本身是一个常量, 底层 const 表示的是 指针所指的对象是一个常量。

更一般情况下, 顶层 const 可以表示任意对象是一个常量,这对于算术类型、类、指针等任何数据类型都是成立的, 底层 const 则与指针和引用等复合类型的基本类型部分有关 ,比较特殊的是,指针既可以是顶层 const 也可以是底层 const ,这一点与其他类型区别明显。

顶层和底层的翻译很容易让人误解为就只有两层,实际上当然是不是的。首先我们假设有这样的代码:

template<typename T> using Const = const T;
template<typename T> using Ptr = T*;
const int *** const shit = nullptr;
//以上等价于
Const<Ptr<Ptr<Ptr<Const<int>>>>> shit = nullptr;

从右向左读,星号读作pointer,没多一层加一个to,然后最前面加上declare就行。比如对const int *** const shit;,可以读作:declare shit as const pointer to pointer to pointer to const int。

其中一个知识点就是
当用实参初始化形参时会忽略掉顶层const。换句话说,形参的顶层const都被忽略掉了,当形参有顶层const时,传给它常量对象或者非常量对象都是可以的

当const作为形参的时候跟复制一样,要注意底层复制,也就是说重点要看是不是在算术类型、类、指针等任何数据类型前加了const,底层const拷入和拷出的对象必须具有相同的底层const资格或两个对象的数据类型可以转化,如非常量可以转换成常量,反过来不行

int i = 0;
int *const p1 = &i; //不能改变p1的值,这是一个顶层const
const int *ci = 42; //不能改变ci的值,这是一个顶层const
const int *p2 = &ci; //允许改变p2的值,这是一个底层const
const int *p2 = &ci; //允许改变p2的值,这是一个底层const
const int *const p3 = p2; //靠右的const是顶层const,靠左的是底层const
const int &r = ci; //用于声明引用的const都是底层const

//当执行对象的拷贝操作,常量是顶层const还是底层const区别明显,其中,顶层const不受什么影响。但是底层const拷入和拷出的对象必须具有相同的底层const资格或两个对象的数据类型可以转化,如非常量可以转换成常量,反过来不行
int *p = p3; //False,p3包含底层const定义,而p没有
p2 = p3; //True;p2和p3都是底层const
p2 = &i; //True,int*能转换成const int*
int &r = ci; //False,普通的int&不能绑定到int常量上
const int &r2 = i; //True,const int&可以绑定到一个普通int上

2. 函数前后加const

当const在函数名前面的时候修饰的是函数返回值。

当const在函数名后面表示是常成员函数,该函数不能修改对象内的任何成员,只能发生读操作,不能发生写操作。 为了声明一个const成员函数, 把const关键字放在函数括号的后面。声明和定义的时候都应该放const关键字。

任何不会修改数据成员的函数都应该声明为const类型。如果在编写const成员函数时,不慎修改了数据成员,或者调用了其它非const成员函数,编译器将指出错误,这无疑会提高程序的健壮性。

#include<iostream>
#include<string>
using namespace std;

class constPractice {
public:
	int length() const;
	const char* getContent();
	void setLengthValid(bool isLengthValid);
private:
	char * content;
	int contentlength; //D
	bool lengthIsValid;//E
};

int constPractice::length() const {
	if (!lengthIsValid) {
		contentlength = strlen(content);//A
		lengthIsValid = true;//B
	}
	return contentlength;
}

const char* constPractice::getContent() {
	return content;
}

void constPractice::setLengthValid(bool isLengthValid) {
	lengthIsValid = isLengthValid;
}

int main() {
	constPractice *cp = new constPractice();
	cp->setLengthValid(false);
	cp->length();
	char* content = cp->getContent();//C
	return 0;
}

在这里插入图片描述

程序报错,原因在于contentlength = content.size(); //A ;lengthIsValid = true; //B;char* content = cp->getContent();//C
由于length()函数后面加了const,因此其为一个不可变对象,但是A,B两处的代码都对其成员变量进行了更改,因此报错。而C处,尝试把一个常量转换为非常量,因此报错。

解决方法: 在类的D、E处的成员前面加上mutable修饰符,在C处前面加const修定符

mutable int contentlength;
mutable bool lengthIsValid;
const char* content = cp->getContent();

从字面的意思知道,mutalbe是“可变的,易变的”,跟constant(既C++中的const)是反义词。在C++中,mutable也是为了突破const的限制而设置的。被mutable修饰的变量,将永远处于可变的状态,即使在一个const函数中。这样在C、D处将不会出错。

参考文章
【1】https://www.zhihu.com/question/24785843/answer/238903938
【2】https://blog.csdn.net/caimagic/article/details/51496666
【3】https://www.cnblogs.com/MATU/p/5283380.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值