《C++覆辙录》——1.6:对常量(性)的认识误区

.6:对常量(性)的认识误区

在C++中的常量性概念是平凡的,但是这和我们对const先入为主的理解不太符合。首先我们要特别注意以const饰词修饰的变量声明和字面常量的区别:

int i = 12;
const int ci = 12;```
字面常量12不是C++概念中的常量。它是个字面常量。字面常量没有地址,永远不可能改变其值。i是个对象,有自己的地址,其值可变。用const关键字修饰声明的ci也是个对象,有自己的地址,尽管在本例中其值不可变。

我们说i和`ci`可以作为左值使用,而字面常量12却只能作为右值。这两个术语来源于伪表达式`L=R`,说明只有左值能出现在赋值表达式左侧,右值则只能出现在赋值表达式右侧。但这种定义对C++和标准C来说并不成立,因为在本例中`ci`是左值,但不能被赋值,因为它是个不可修改的左值。

如果把左值理解为“能放置值的地方”,那么右值就是没有与之相关的地址的值。

int *ip1 = &12; // 错误!
12 = 13; // 错误!
const int *ip2 = &ci; // 没问题
ci = 13; // 错误! `
最好仔细考虑一下上面ip2的声明中出现的const,这个饰词描述了对我们通过ip2ci的操作的约束,而不是对于ci的一般操作的约束21。如果我们想声明指涉到常量的指针,我们应该这么办:

const int *ip3 = &i;
i = 10; // 没问题,约束的不是i的一般操作而是通过ip3对i的操作
*ip3 = 10; // 错误!  ```
这里我们就有了一个指涉到`const int`型别的指针,而这个`const int`型别对象又是一个普通int型别对象的引用。这里的常量性仅仅限制了我们能通过ip3做什么事。这不表明i不会变,只是对它的修改不能通过ip3进行。如果我们再把问题说细一点,请看下面这个把`const`和`volatile`结合使用的例子:

extern const volatile time_t clock;`
这个const饰词的存在表明我们未被允许(在代码中显式地直接)修改变量clock的值,但是同时存在volatile饰词说明clock的值肯定还是会通过其他途径发生变更22。

时间: 2024-09-25 17:30:26

《C++覆辙录》——1.6:对常量(性)的认识误区的相关文章

《C++覆辙录》——导读

前言 C++覆辙录 本书之渊薮乃是近20年的小小挫折.大错特错.不眠之夜和在键盘的敲击中不觉而过的无数周末.里面收集了普遍的.严重的或有意思的C++常见错误,共计九十有九.其中的大多数,(实在惭愧地说)都是我个人曾经犯过的. 术语"gotcha"1有其云谲波诡的形成历史和汗牛充栋的不同定义.但在本书中,我们将它定义为C++范畴里既普遍存在又能加以防范的编码和设计问题.这些常见错误涵盖了从无关大局的语法困扰,到基础层面上的设计瑕疵,再到源自内心的离经叛道等诸方面. 大约10年前,我开始在

《C++覆辙录》——常见错误1:过分积极的注释

第1章 基础问题 C++覆辙录 说一个问题是基础的,并不就是说它不是严重的或不是普遍存在的.事实上,本章所讨论的基础问题的共同特点比起在以后章节讨论的技术复杂度而言,可能更侧重于使人警醒.这里讨论的问题,由于它们的基础性,在某种程度上可以说它们普遍存在于几乎所有的C++代码中. 常见错误1:过分积极的注释 很多注释都是画蛇添足,它们只会让源代码更难读,更难维护,并经常把维护工程师引入歧途.考虑下面的简单语句: a = b; // 将b赋值给a 这个注释难道比代码本身更能说明这个语句的意义吗?因而

《C++覆辙录》——1.5:对引用的认识误区

1.55:对引用的认识误区 对于引用的使用,主要存在两个常见的问题.首先,它们经常和指针搞混.其次,它们未被充分利用.好多在C++工程里使用的指针实际上只是C阵营那些老顽固的杰作,该是引用翻身的时候了. 引用并非指针.引用只是其初始化物的别名.记好了,能对引用做的唯一操作就是初始化它.一旦初始化结束,引用就是其初始化物的另一种写法罢了(凡事皆有例外,请看常见错误44).引用是没有地址的,甚至它们有可能不占任何存储: int a = 12; int &ra = a; int *ip = &r

《C++覆辙录》——1.7:无视基础语言的精妙之处

1.7:无视基础语言的精妙之处 大多数C++软件工程师都自信满满地认为自己对所谓C++的"基础语言",也就是C++继承自C语言的那部分了如指掌.实际情况是,即使经验丰富的C++软件工程师,有时也会对最基础的C/C++语句和运算符的某些妙用一无所知. 逻辑运算符不能算难懂,对吗?但刚入行的C++软件工程师却总是不能让它们物尽其用.你看到下面的代码时是不是会怒从胆边生? bool r = false; if( a < b ) r = true;``` 正解如下: bool r = a

《C++覆辙录》——第2章 语法问题2.1:数组定义和值初始化的语法形式混淆

第2章 语法问题 C++覆辙录C++语言的语法和词法结构博大精深.此复杂性的一部分是从C语言那里继承而来的,另一部分则是为支撑某些特定的语言特性所要求的. 本章中我们将考察一组语法相关的头疼问题.其中有些属于常见的手误,但是错误的代码仍然能够通过编译,只不过会以出人意料的方式运行罢了.另外一些则是由于一段代码的语法结构及它们的运行期行为不再互为表里.其余的部分,我们主要研究语法层面的灵活余地带来的问题:明明是一字不差的代码,不同的软件工程师能从中得出大相径庭的结论来. 2.1:数组定义和值初始化

《C++覆辙录》——2.6:声明饰词次序的小聪明

2.6:声明饰词次序的小聪明 就语言本身所限,声明饰词孰先孰后纯属无关紧要的形而上之争:`int const extern size = 1024; // 合法,但有离奇不经之嫌 `无论如何,如果没有令人信服的理由去背离习惯用法,那么顶好还是接受有关声明饰词次序事实上的标准:先写连接饰词,再写量化饰词,再写型别. extern const int size = 1024; // 正常下面这个指针的型别是什么呀? int const *ptr = &size; ``` 对,这是一个指涉到常量整数型

《C++覆辙录》——2.8:效果漂移的型别量化饰词

2.8:效果漂移的型别量化饰词 内建数组不可能有常量性或挥发性,所以修饰它的型别量化饰词(const或volatile)的效果实际上会漂移,转而应用到其持有物的某个适当位置: typedef int A[12]; extern const A ca; // 由12个常量整数型别元素构成的数组 typedef int *AP[12][12]; volatile AP vm; // 指涉到整数型别元素的挥发性指针构成的二维数组``` volatile int *vm2[12][12]; // 指涉到

《C++覆辙录》——1.8:未能区分可访问性和可见性

1.8:未能区分可访问性和可见性 C++语言压根儿没有实现什么数据隐藏,它实现了的是访问层级.在class中具有protected和private访问层级并非不可见,只是不能访问罢了.如同一切可见而不可及的事物一样(经理的形象跃入脑海),他们总是惹出各种麻烦. 最显而易见的问题就是即使是class的实现仅仅更改了一些貌似不可见的部分,也会带来必须重新编译代码的苦果.考虑一个简单的class,我们为其添加一个新的数据成员: class C { public: C( int val ) : a_(

《C++覆辙录》——1.9:使用糟糕的语言

1.9:使用糟糕的语言 当一个更大的世界入侵了C++社群原本悠然自得的乐土之时,它们带来了一些足堪天谴的语言和编码实践.本节乃是为了厘清返璞归真的C++语言所使用的正确适当.堪称典范之用语和行为. 用语 表1-1列出了最常见的用语错误,以及它们对应的正确形式. 表1-1 常见用语错误及其对应正确用语 没有什么所谓"纯虚基类".纯虚函数是有的,而包含有或是未能改写(override)此种函数的类,我们并不叫它"纯虚基类",而是叫它"抽象类". C+