《C++覆辙录》——2.9:自反初始化

2.9:自反初始化

在以下的代码里,var的值变成了多少?

int var = 12;
{
  double var = var;
  // ...
}```
未有定义。C++语言中,某个名字在它的初始化对象被解析到之前就进入了其辖域的话,在初始化对象引用到这个名字时,它引用到的不是别的,正是这个刚刚被声明的对象。没有几个软件工程师会写出像上面这么莫名其妙的声明代码,但也许复制、粘贴的手法会让你陷入困境:

int copy = 12; // 某深藏不露的变量
// ...
int y = (3x+2copy+5)/z; // 将y的赋值运算符的右手边操作数剪切……
// ...
void f(){
  // 这里需要y的初始化值
  int copy = (3x+2copy+5)/z; // 把上面的剪切内容粘贴到此
  }`
用预处理符号的话,你会犯和恣意复制、粘贴的行为完全一样的错误(参见常见错误26):

int copy = 12; // 某深藏不露的变量
// ...
#define Expr ((3*x+2*copy+5)/z);
// ...
void g(){
// 这里需要y的初始化值
  int copy = Expr; // 噩梦重现
  }```
此问题的另一种表现形式就是命名时把型别的名字和非型别的名字弄混了:

struct buf{
  char a, b, c, d;
};
// ...
void aFunc(){
  char *buf = new char[sizeof(buf)];
  // ... `
那个局域里的buf很可能会获取4字节的内存,足够放置一个char *。这个错误可能会很久都校验不出来,尤其在型别struct buf和指针型别变量buf`具有相同大小的时候23。遵守一个把型别和非型名的名字区分清楚的命名约定就可以在这个问题上防患于未然(参见常见错误12):

struct Buf{
  char a, b, c, d;
};
// ...
void aFunc(){
  char *buf = new char[sizeof(Buf)]; // 没问题
  // ...
}```
现在我们知道怎么解决下面这样的问题了:

int var = 12;
{
  double var = var;
  // ...
}
但它的变形呢?

const int val = 12;
{
  enum {val = val};
  // ...
}`
枚举量val的值是多少?未有定义吗?再猜一次。正确答案是其值为12,理由是枚举量的声明位置,与变量不同,是在它的初始化对象(严格地说,是枚举量的定义)之后的。“=”之后的那个val,是在外层辖域中的常量。这段讨论把我们带入了一个更错综复杂的局面:

const int val = val;
{
  enum {val = val};
  // ...```
时间: 2024-08-16 06:37:10

《C++覆辙录》——2.9:自反初始化的相关文章

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

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

《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.3:全局变量

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

《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++覆辙录》——1.2:幻数

2:幻数 幻数,用在这里时其含义是上下文里出现的裸字面常量(raw numeric literal),本来它们应该是具名常量(named constant)才对: class Portfolio { // ... Contract *contracts_[10]; char id_[10]; };``` 幻数带来的主要问题是它们没有(抽象)语义,它们只是个量罢了.一个"10"就是一个"10",你看不出它的意思是"合同的数量"或是"标识符

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

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

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

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