欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

C++ const的那些事

程序员文章站 2022-05-04 19:44:39
...

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;
}

C++ const的那些事

程序报错,原因在于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

相关标签: C++ c++