我正在阅读《Thinking in C++》第6章“初始化和清理”。作者说:
实际上,编译器更可能遵循C语言的做法,在该作用域的左花括号处分配全部存储空间。这并不重要,因为作为程序员,在定义之前你不能访问存储空间。尽管存储空间在块的开头分配,但构造函数的调用直到对象在序列点中被定义才发生,因为在此之前标识符不可用。编译器甚至会检查确保你不会把对象定义放在条件传递该序列点的位置,例如在switch语句中或某个goto可以跳过它的位置。
然后作者给出了以下示例:
我不明白上面的代码为什么可以正常工作?根据我的理解,如果i不是1,
补充说明: 这句话 "实际上更可能的是编译器会遵循C中分配作用域存储的惯例,在该作用域的左括号处分配所有存储。" 也让我感到困惑。 根据作者的描述,在
实际上,编译器更可能遵循C语言的做法,在该作用域的左花括号处分配全部存储空间。这并不重要,因为作为程序员,在定义之前你不能访问存储空间。尽管存储空间在块的开头分配,但构造函数的调用直到对象在序列点中被定义才发生,因为在此之前标识符不可用。编译器甚至会检查确保你不会把对象定义放在条件传递该序列点的位置,例如在switch语句中或某个goto可以跳过它的位置。
然后作者给出了以下示例:
class X {
public:
X();
};
X::X() {}
void f(int i) {
if(i < 10) {
//! goto jump1; // Error: goto bypasses init
}
X x1; // Constructor called here
jump1:
switch(i) {
case 1 :
X x2; // Constructor called here
break;
// case 2 : // Error: case bypasses init
X x3; // Constructor called here
break;
}
}
int main() {
f(9);
f(11);
}///:~
我不明白上面的代码为什么可以正常工作?根据我的理解,如果i不是1,
x2
就可以跳过初始化。补充说明: 这句话 "实际上更可能的是编译器会遵循C中分配作用域存储的惯例,在该作用域的左括号处分配所有存储。" 也让我感到困惑。 根据作者的描述,在
switch
的左括号处,编译器已经为 x2
和 x3
分配了空间。如果是这样,那么有机会使 x2
没有初始化(情况1不满足)。
case 2
并绕过x2
的初始化,在 C++ 中是不允许的。正如书中所说: "编译器甚至会检查以确保您没有将对象定义放在条件通过它的序列点处,例如在 switch 语句或某个 goto 可以跳过它的地方。" - Jonathan Wakelyx2
不在作用域时保留的内存。这就是Eckel所说的:尽管编译器事先知道对象将在内存中的位置,但程序员在安全地初始化保留的内存之前不能使用该对象。 - Jonathan Wakely