C++中的const关键字介绍

电子说

1.2w人已加入

描述

前言

前一篇文章我们主要介绍了C++中的复合类型引用和指针,这篇文章我们将会主要介绍C++中const关键字。有时候我们想定义一个值不能被改变的变量,例如我们想使用一个变量存储buffer的大小,如果我们不希望这个值被改变,那么我们就可以使用const关键字。

const int bufSize = 512;

现在已经定义了一个const变量bufSize,如果想对const变量赋值就会报错

bufSize = 256 //会报错

const初始化

因为在我们创建一个const对象后就没有办法改变const对象的值,所以其必须被初始化。

const int j = get_size(); //可以运行时初始化
const int i = 42//可以在编译时初始化
const int k; //错误,k没有被初始化

正如之前我们多次提到的,一个对象的类型决定来其所有支持的操作,一个const类型可以使用绝大多数并不是全部非const类型的操作,唯一的限制就是我们使用的操作不能改变const对象的值,例如我们可以在算术表达式中将const类型的变量当作非const类型的变量使用,因为其没有改变const变量的值。

当一个const对象在编译期间就被一个常量初始化了,例如我们上述例子提到的bufSize在编译期间就被初始化为512,在编译期间编译器就会将所有使用到bufSize的地方替换为512。为了替换变量的值,编译器必须要知道变量的初始化结果,为了知道初始化结果,所有需要使用该变量的文件都需要定义该变量,为了支持这种用法,同时避免定义多个相同的变量,const变量对于文件来说是本地的,当我们定义在不同的文件中定义多个同名的const变量,它们表现起来就像我们在不同的文件定义了不同的变量。

但是有时候我们希望在不同的文件中共享同一个const变量,但是其初始化并不是一个常量,我们并不希望编译器为每一个文件生成单独的变量,而是希望想非const变量一样,我们希望在一个文件定义,然后在另一个文件声明且使用该const变量,这时候就要使用extern关键字

//file1.cc定义切初始化了一个const变量
extern const int bufSize = fcn();

//file1.h
extern const int bufSize;

指向常量的引用

正如其他的对象,我们也可以将一个引用绑定到一个const类型的对象,为此我们可以使用一个引用指向,不同于一般的引用,一个指向常量的引用无法改变该引用指向的对象,这也很好理解,因为该对象是一个常量,而常量一旦定义就无法更改。

const int ci = 1024;
const int &r1 = ci; //正确,引用和引用指向的对象都是常量
r1 = 42; //错误,r1指向的是一个常量,无法改变一个常量的值
int &r2 = ci; //错误,无法让一个非常量的引用指向一个常量

❝一些开发这习惯于将指向常量的引用简称为常量引用,这个简称看上去说的通,但是你需要记住这就是个简称,从技术的角度来说是没有常量引用的,因为一个引用不是一个对象,所以我们无法让一个引用本身是一个常量。

指向常量的引用未必指向一个常量

从之前的例子中我们可以看到指向一个常量的引用必须是一个常量引用,否则会报错,但是一个指向常量的引用不一定指向一个常量,只是不同通过该引用来改变该对象的值,这么说可能有点绕,可以用以下例子说明

int i = 42;
int &r1 = i; //r1是指向i的引用
const int &r2 = i; //r2是指向i的常量引用,但是不能通过r2改变i的值
r1 = 0; //r1是非常量引用,可以通过r1改变i的值
r2 = 0; //r2是常量引用,无法通过r2改变i的值

指针与常量

与引用相同,指针可以指向常量也可以指向非常量,同样指向常量的指针无法通过该指针来改变所指向的常量的值。

const double pi = 3.14; \\\\pi是一个常量,其值不可以改变
double *ptr = π //错误 ptr不是一个指向常量的指针,所以无法指向pi
const double *cptr = π //正确,cptr是指向常量的指针
*cptr = 42; //错误,cptr指向的是常量,其值无法改变

还是与引用相同,我们也可以让一个指向常量的指针指向一个非常量,但是不可以通过该指针改变该对象的值

double dval = 3.14;
cptr = &dval; //正确,但是无法通过cptr改变dval的值

常量指针

与引用不同,指针本身是一个对象,所以其本身可以是常量,常量指针与其他的常量性质是相同的,常量指针必须初始化,且一旦初始化该指针持有的地址也将不会改变,那么该如何声明一个指针呢,那就是在*后加上const,例子如下

int errNumb = 0;
int *const curErr = &errNumb; //curErr是一个常量指针,将会一直指向errNumb
const double pi = 3.1425926;
const double *const pip = π //pip是一个常量指针且指向一个常量

常量表达式

常量表达式是指其返回值在编译阶段就能计算出来且不会改变的表达式,一个对象是否是常量表达式取决于其类型和初始化结果,例子如下

const int max_files = 20; //是
const int limit = max_files; //是
int staff_size = 27; //不是,因为其类型不是const
const int sz = get_size(); //不是,因为get_size()需要在运行时才能计算出来

constexpr

在一个大型的系统中有时候我们很难去确定初始化的结果是否是常量表达式,也许我们定义来一个常量并且用一个我们以为的常量表达式去初始化,但是一些场景下我们需要一个常量表达式但是最后返回不是一个常量表达式,那么一个对象在定义和使用的情况并不一致。在新标准下为了解决这个问题,提供了constexpr关键字,如果一个变量被constexpr修饰,那么编译器将会检查该变量是否是一个常量表达式。

constexpr int mf = 20; //20是常量表达式
constexpr int limit = mf + 1; //mf + 1是常量表达式
constexpr int sz = size(); //正确,如果size constexpr函数,这个之后文章会介绍

指针与constexpr

需要注意的是如果我们定义一个指针在constexpr表达式中,那么constexpr是作用于指针,而不是指针指向的值。

const int *p = nullptr; //p是一个指向常量的指针
constexpr int *q = nullptr; //q是一个常量指针
constexpr const int *cptr = nullptr; //cptr是一个常量指针,且指向一个常量
打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分