《C++ primer》英文第五版阅读笔记(五)——const

Chapter2,Section2.4——const Qualifier


const常量


被const修饰的变量的值不能被改变。

被const修饰的变量必须在定义时进行初始化。

const要放在类型名前面。


初始化

const对象在不改变其本身的值时能够做大多数非const对象可以进行的操作。

例如:我们可以在一个算数表达式中像普通对象那样使用const对象,也可以像普通对象那样获得const对象的bool值。

在所有的操作当中,有一个不改变值的操作是初始化——当我们用一个对象去初始化另一个对象时,这个两个对象是否其中一个是const对象或者都是const对象是无关紧要的。也就是说,一个const对象可以给非const对象赋值,一个非const对象可以给const对象赋值,两个const对象之间也可以赋值。赋值成功之后,新的对象和源对象就没有什么关系了。


当一个const对象是在编译时用常数初始化时,编译器在编译时与该对象相关的地方都替换成这个常数。

当多个文件中都要用到同一个const对象时,而我们又不想在每个文件中分别对这个const对象进行定义。我们想让该const对象像其他非const对象一样。希望在一个文件中进行定义,在其他文件中进行声明。

为了定义一个const变量的单一实例,我们在它的定义和声明中使用关键字extern。

例:extern const int bufSize = fcn(); //是一个定义,因为是const类型,所以可以用在其他文件中。

extern const int bufSize;//是一个声明,bufSize不是这个文件的本地对象,关于它的声明可以发生在任何地方(文件)。


在多个文件中分享同一个对象,必须把这个对象定义成extern。


(一)const的引用

const的引用的值不能改变。(也就是不能改变它所绑定的对象的值。)

一个引用被定义为const或是非const影响的是我们能用这个引用进行什么操作,而不是我们是否能修改它所绑定的对象。


初始化:“引用的类型和它绑定的对象类型必须匹配的例外1”:

我们可以将const引用绑定在一个非const对象,const对象,一个literal,或是一个普通的表达式上。

非const引用类型不能绑定一个const引用类型。


如果有下面的语句:

double dval = 3.14;const int &ri = dval;

这是允许的,编译器将这段代码转换为类似下面这样:

double dval = 3.14;const int temp = dval; const int &ri = temp;

在这种情况下,ri绑定的是一个临时变量。临时变量是一个无名的对象,它在程序需要存储计算表达式返回的结果时被编译器创建。

如果这种初始化是允许的,但是ri不是const类型的会发生什么情况?

在这种情况下,就能改变ri所绑定的对象的值,但是那是一个临时变量而不是dval。程序开发者可能希望通过给ri赋值从而改变dval的值,毕竟,改变ri为什么不能改变ri绑定的对象?那是因为绑定一个临时变量总是不能达到程序开发者期望的结果,所以在这门语言中这是非法的。这就是之前提到的“引用的类型和它绑定的对象类型必须匹配“,而const是其中的一个例外。


(二)const修饰的指针

const double pi = 3.14;

const double *cptr = π

一个被const修饰的指针不能够被用来改变它所指向的对象的值。*cptr = 42;//非法

一个被const修饰的对象的地址只能存储在一个被const修饰的指针里面。

之前提到过说:”指针的类型和它所指对象的类型必须相同“,这里有一个例外1:

一个被const修饰的指针可以指向一个未被const修饰的对象。(但是不能通过该const指针改变这个非const对象)

一个被const修饰的指针既可以指向被const修饰的对象也可以指向未被const修饰的对象。


与引用类似,定义一个被修饰的const指针仅仅影响我们能对这个指针进行什么操作。一个被const修饰的指针指向的对象也可能改变。


const 指针

指针自己就是const的。和其他const对象一样,一个const指针必须被初始化,一旦初始化了,它的值(它存储的地址)就不再改变了。

定义方式:在*后面加上const。

例:const double pi = 3.1416;

const double *const pip = π

int errNumb = 0;

int *const curErr = &errNumb;

"从右向左读"。


curErr是一个const指针,指向int类型对象。

pip是一个const指针,指向const double类型。


一个const指针是否能改变它所指向的对象取决于这个const指针指向的对象的类型。

例如:pip是一个const指针指向const修饰的对象,所以pip所指向的对象的值不能通过pip进行改变,并且pip中存的地址也不能改变。

curErr是一个const指针但是指向非const对象,所以我们能用curErr去改变errNumb的值。


前面的const指的是指针所修饰的对象不能通过指针进行改变,后一个const指的是指针本身存储的地址不能改变。


顶层const(top-level const)

当一个指针自己本身是const类型时,他就是顶层const。

顶层const可以出现在任何类型中:内置算术类型,类类型,或者是一个指针类型。

底层const(low-level const)出现在复合类型中的基本类型,比如:引用和指针。

指针可以既是顶层const又是底层const。


顶层const和底层const的区别是在进行复制(赋值)时。

当进行赋值操作时,顶层const常常被忽略。但是,底层const从来都不能被忽略。

当进行赋值操作时,两个对象必须具有相同的底层const或者两个对象之间必须进行类型转换。

通常我们可以把一个非const引用或指针转换成const引用或指针,但是反过来就不行了。

一个指针可以既有顶层const又有底层const,通常进行赋值时不考虑顶层const。


常量表达式(constexpr)和常数表达式(Constant Expression)

常数表达式的值不能改变,并且它的值能在编译期间计算出来。literal是常数表达式。被常数表达式初始化的const对象也是常数表达式。

一个给定的对象或表达式是否为常数表达式取决于类型和初始化。

常数表达式的值必须在编译时能计算出来。


通常,我们无法判断一个初值设定是否为常数表达式。在新标准里:

我们在变量声明中加入”constexpr“来表示这个变量是一个常数表达式。

作为”constexpr“声明的变量是const的,并且必须被常数表达式进行初始化。

当用方法的返回值作为constexpr变量的初始值时,这个函数必须是constexpr的。这种函数必须非常简单以至于编译器能在编译期间得到它们的返回值。


通常,在要作为常数表达式的变量中使用constexpr是一个很好的主意。


Literal类型

因为常数表达式的值要在程序编译期间计算出来,所以能使用constexpr声明的类型是有限的,这些类型是”literal类型“,因为它们能够很简单获得literal值。在我们目前为止使用的类型里,算数类型,引用和指针类型是literal类型。我们的Sales_item类和库IO和String类型不是literal类型,今后,我们不能将这些类型定义为constexpr。还有其他的literal类型在以后介绍。


尽管我们能把引用和指针定义为constexpr,但是对它们的初始化很严格。constexpr指针能够被nullptr或者0进行初始化,也可以用一个固定的地址进行初始化(指向固定地址的对象)。


定义在函数内的变量通常没有固定的地址,因此不能用constexpr指针指向这种变量。一个定义在任何函数体外的对象的地址是一个常数表达式,所以可以用它来初始化constexpr指针。函数可以定义在调用该函数的调用中存在的量?,这些特殊的本地对象和定义在任何函数外的对象一样,都具有固定的地址。一个constexpr引用可能绑定在这种变量上,一个constexpr指针也可能被这种变量初始化。


指针和constexpr

当把一个指针定义为constexpr时,说明符constexpr指的是这个指针本身,而不是指它所指向的类型。constexpr相当于定义了一个顶层const对象。

和其它指针常量一样,constexpr指针可能指向const类型或非const类型。




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值