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

2.6:声明饰词次序的小聪明

就语言本身所限,声明饰词孰先孰后纯属无关紧要的形而上之争:
`
int const extern size = 1024; // 合法,但有离奇不经之嫌 `
无论如何,如果没有令人信服的理由去背离习惯用法,那么顶好还是接受有关声明饰词次序事实上的标准:先写连接饰词,再写量化饰词,再写型别。

extern const int size = 1024; // 正常下面这个指针的型别是什么呀?
int const *ptr = &size;  ```
对,这是一个指涉到常量整数型别的指针。但你根本难以置信有多少软件工程师会把它误读成一个指涉到一般整数型别的常量指针18:
`
int * const ptr2 = &size; // 错误!  `
以上是两种完全不同的型别。当然了,第一种指针型别可以指涉到一个常量整数型别,第二种不行19。很多软件工程师会随口把“指涉到常量型别的指针”念成“常量指针”,这不是一个好习惯,它只会把你要表达的真实意思(“指涉到常量型别的指针”)传达给那些粗枝大叶之徒,而真正字斟句酌的称职后生则会被你的言辞误导(理解成“指涉到一般型别的常量指针”)。

当然需要承认的是,标准库里有一个字面上表示“常量迭代器”之意的`const_iterator`概念,它无可救药地实际上表示一个“指涉到常量元素的迭代器”,而这些迭代器自身却并不具常量性(标准委员会的家伙们某天吃错了药不是你要向他们学坏的理由)。仔细区分“指涉到常量型别的指针”和“常量指针”(参见常见错误31)。

由于声明饰词次序在技术层面上无关紧要,一个指涉到常量的指针可以以两种形式声明:

const int *pci1;
int const *pci2; `
有些C++专家比较提倡第二种书写形式,因为他们认为对于更复杂的指针型别声明来说,这种写法更容易读:

int const * const * pp1;  ```
把量化饰词const置于若干声明饰词的最后,这样我们就可以倒过来读所有的指针型别的饰词。从右到左,pp1的指涉物是一个常量(`const`)指针,后者指涉到一个整数常量(`const int`)。而习惯的饰词次序则不支持这样的平凡规则:

const int const pp2; // pp2的型别和pp1完全相同⑦`
⑦译者注:本书作者显然在后来的岁月里改变了他的有关饰词次序的看法,变得更加包容。他在本书里把两种饰词的次序中的一个打上了“不推荐”的烙印,显然觉得两种用法里一种优于另一种。但他在另一本比较晚近出版的书中谈到这个问题时,就表示两种用法选择哪一种“无关紧要”了。参见(Dewhurst, 2006),条款7。而Scott Meyers在谈到量化饰词应该放在型别前还是后时,则更直截了当地说“你应该同时适应两者”,参见(Meyers, 2006),条款3。根据这些材料及其变迁的历史轨迹,我们可以说,读别人的代码时,不应该误读,而自己在撰写代码时则纯属风格问题,可以根据自己的理解方向和喜好来选择一种,并固定下来,在编码实践中沉淀为自己的代码风格的一部分。
前一种饰词次序的安排也没有带来太多复杂性,一个C++维护工程师若是在要阅读和维护的代码里存在这样的片段,他也应该是有能力搞定的。更重要的是,指涉到指针的指针或是其他类似的声明是较少见的,尤其少见于交由C++新手打理的接口代码里。典型情况是,它们藏匿于基础实现码的深处。平凡的、直截了当的指涉到常量的指针就常见得多。所以,还是遵循习惯用法以避免误解较佳:

const int *pci1; // 正确:指涉到常量的指针

时间: 2024-09-20 09:05:30

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

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

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

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

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

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

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

《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.5:对引用的认识误区

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

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

.6:对常量(性)的认识误区 在C++中的常量性概念是平凡的,但是这和我们对const先入为主的理解不太符合.首先我们要特别注意以const饰词修饰的变量声明和字面常量的区别: int i = 12; const int ci = 12;``` 字面常量12不是C++概念中的常量.它是个字面常量.字面常量没有地址,永远不可能改变其值.i是个对象,有自己的地址,其值可变.用const关键字修饰声明的ci也是个对象,有自己的地址,尽管在本例中其值不可变. 我们说i和`ci`可以作为左值使用,而字面常

《C++覆辙录》——1.11:聪明反被聪明误

1.11:聪明反被聪明误 C++语言和C语言看起来会吸引相当多的人去张扬个性(你有没有听说过一个叫"Obfuscated Eiffel"的比赛?)46.在这些软件工程师的思维里,两点间的最短距离是普通欧氏空间之球面扭曲上的大圆. 试举一例:在C++语言的圈子里(且不论这个圈子是不是普通欧氏空间里的),代码的排版格式纯粹是为了方便解读代码的人类的,而对于代码47的意义,只要语汇块的次序还是按原先的次序的依次出现,就怎么都无所谓.这最后一个附加条款殊为重要,比如,以下这两行表示的是非常不同

《C++覆辙录》——2.10:静态连接型别和外部连接型别

2.10:静态连接型别和外部连接型别 根本没有本条款名称所述的这类东西.但是,经验丰富的C++软件工程师却常常写出好像把连接类型饰词应用于型别的声明语句,把刚入道的C++新手带坏了: static class Repository{ // ... } repository; // 静态连接的 Repository backUp; // 不是静态连接的``` 也许确实可以说某种型别有连接类型,但是连接类型饰词却总是绑定到对象或函数,而不是型别的.如此说来还是写得清楚些好: class Reposi

《C++覆辙录》——1.3:全局变量

1.3:全局变量 很难找到任何理由去硬生生地声明什么全局变量.全局变量阻碍了代码重用,而且使代码变得更难维护.它们阻碍重用是因为任何使用了全局变量的代码就立刻与之耦合,这使得全局变量一改它们也非得跟着改,从而使任何重用都不可能了.它们使代码变得更难维护的原因是很难甄别出哪些代码用了某个特定的全局变量,因为任何代码都有访问它们的权限. 全局变量增加了模块间的耦合,因为它们往往作为幼稚的模块间消息传递机制的设施存在.就算它们能担此重任,从实践角度来说8,要从大型软件的源代码中去掉任何全局变量都几乎不