const常量限定符

const的初始化及作用

对类型添加关键字const修饰后,其值不能被改变。

	const int bufSize = 512;	//	正确:const修饰的变量必须进行初始化
	const int j = get_size();	//	正确:const修饰的变量初始值可以是任意复杂的表达式
	bufSize = 1024;		//	错误:修饰const后不能进行修改

const在多文件下的声明并使用

默认情况下,const对象被设定为仅在文件内有效。当多个文件中出现了同名的const变量时,其实等同于在不同文件中定义了独立的变量。

如果想要只在一个文件中定义const,而在其他多个文件中声明并使用。解决办法是,对于const变量不管是声明还是定义都添加extern关键字(extern会将修饰变量设为全局变量,各个文件皆可使用)。

	// study.h中声明a
	extern const int a; 
	// study.cpp中定义a
	#include <iostream>
	#include "study_1.h"
	extern const int a = 10;
	int main()
	{
		std::cout << qwe() << std::endl;
		std::cout << a << std::endl;
	}
/*
输出结果为:
10
10
*/
	//	study_1.h中使用a
	#include <iostream>
	#include "study.h"
	int qwe() {
		return a;
	}

注:在C++中extern关键字修饰之后,重复使用include引入其处于的头文件会造成重复定义

const对象与引用

将引用绑定到const对象上,称之为对常量的引用,该引用也不能用作修改它所绑定的对象的值。

	const int i = 20;	//	声明一个常量
	i = 21;	//	错误:i不能修改
	const int& j = i;	
	j = 21;	//	错误:j为对常量的引用,不能修改

	int m = 40;
	const int& n = m;	//	允许为一个常量引用绑定非常量对象
	n = 41;	//	错误:常量引用不能修改值
	m = 41;	//	正确:非常量可以进行修改,此时n的值也变为41

在const对象的引用中有一个特殊的情况,引用可能无法绑定到预想的对象。在普通引用中引用的类型和目标对象的类型必须一致,但在常量引用中,不同的类型之间进行绑定可能会导致绑定在一个临时量上。

	double q = 3.14;
	const int& p = q;
	//	上两行代码在编译器中变成如下形式
	//	double q = 3.14;
	//	const int temp = q;
	//	const int& p = temp;	//	因此p其实绑定的是一个临时量
	std::cout << p << std::endl;	//	值为3,因为进行类型转换,舍弃了小数点后的值
	q = 5.5;	
	std::cout << p << std::endl;	//	值依然为3,因为绑定的变量不是q

注:尽量避免临时量的形式,因为其脱离了设计引用的初衷。

const对象与指针

指向常量的指针(底层const)

指向常量的指针不能用于改变其所指对象的值,但是指针指向的对象可以换。此处为底层const,一般是与指针和引用等复合类型部分有关。

const double m1 = 3.14;
double *n1 = &m1;	//	错误:n1是一个普通指针
const double *n2 = &m1;	//	正确:n2是一个指向常量的指针
*n2 = 42;	//	错误:指向常量的指针,其不能修改指向的对象的值
const double m2 = 6.28;
n2 = &m2;	//	正确:指向常量的指针,可以修改其指向的对象。

注:指向常量的指针必须也加const进行修饰。(二者以下区别处也有说明)

常量指针(顶层const)

常量指针必须初始化,初始化后,其指向的对象就不能再改变。此处为顶层const,顶层const表示任意对象是常量,一般是与数据类型相关,如算术类型、类、指针等。

int n =0;
int *const m = &n;	//	指针m将一直指向n。const为顶层const
const double n1 = 3.14;	//	const为顶层const
const double *const m1 = &n1;	//	指针m1将一直指向n1,同时n1的值也不能改变。从左往右依次是底层const、顶层const

顶层const与底层const在拷贝操作时的区别

底层const相比于顶层const来说在拷入拷出时必须具有相同的底层const资格,或者两个对象的数据类型能够转换

	int m = 0;
	int* const m1 = &m;	//	顶层const,常量指针
	const int* m2 = &m;	//	底层const,指向常量的指针

	int* n1 = m1;	//	正确:顶层const指针不影响
	int* n2 = m2;	//	错误:底层const指针需要具有相同的底层const资格,或者两个对象数据类型能够转换
	const int* n3 = m2;	//	正确:n3与m2具有相同的底层const资格
	const int n3 = m;	//	正确:n3与m两个对象数据类型能够转换,也就是非常量能转换为常量

顶层const与底层const对于函数传参的影响

同上面顶层const与底层const在一般情况下一样遵循那些原则。

void fcn(const int i){ /* 函数体 */}		//	实参初始化形参时会忽略掉顶层const,传入常量对象或者非常量对象都是可以的,但是在函数体内部,i是不能被修改的

int i = 0;
const int ci = i;
string::size_type ctr = 0;
reset(&i);	//	调用形参类型是int*的reset函数
reset(&ci);	//	错误:不能用指向const int对象的指针初始化int*
reset(i);	//	调用形参类型是int&的reset函数
reset(ci);	//	错误:不能把普通引用绑定到const对象ci上
reset(42);	//	错误:不能把普通引用绑定到字面值上
reset(ctr);	//	错误:类型不匹配

//	如果不需要修改引用的值,尽量设为常量引用,如下:如果不是常量引用的话,会容易出错
string q(const string& s) {
	return s;
}

int main()
{
	cout<<q("213");
}

constexpr和常量表达式

常量表达式是指值不会改变并且在编译过程中就能得到计算结果。在复杂系统中,很难分辨一个初始值是不是常量表达式,通过将变量声明为constexpr类型以便由编译器来验证变量的值是否是一个常量表达式。

constexpr int mf =20;	//	20是常量表达式
constexpr int limit = mf + 1;	//	mf + 1是常量表达式
constexpr int sz = size();	//	当size是一个constexpr函数时才正确

一般来说,如果认为一个变量是一个常量表达式,就声明为constexpr类型。
注:用constexpr声明的指针对象置为顶层const。

const int *p = nullptr;	//	p是一个指向常量的指针
constexpr int *q = nullptr;	//	q是一个常量指针

类型别名中的常量

const加在别名指针前还是对别进行修饰,如下代码,a是一个指向char的指针,在两个声明语句中const修饰的都是a,也就是修饰指针,所以是常量指针。

typedef char *a;
const a b =0;	//	b是指向char的常量指针
const a *c;	//	c是指针,指向的对象是一个指向char的常量指针

常量与auto

auto一般会忽略掉顶层const,底层const会保留下来。

const int i = 50;
auto j = i;	//	忽略掉顶层const,自动推演出的类型为int
const auto m = i;	//	此处得到的类型为const int
const int *n =&i;
auto a = n; //	此处得到的是const int

常量与decltype

decltype使用的表达式是一个变量,则返回该变量的类型(包括const和引用在内)。

const int i = 0, &j = i;
decltype(i) x = 0;	//	x的类型是const int
decltype(j) y = x;	//	y的类型是const int&
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值