7

我正在阅读 C++ 第 6 章初始化和清理中的思考。作者说:

实际上,编译器更有可能遵循 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);
}///:~

我不明白为什么上面的代码可以?据我了解,如果不是x2可以绕过初始化。i1

补充:

这句话“实际上,编译器更有可能遵循 C 中的做法,在该范围的左大括号处为一个范围分配所有存储空间。” 也让我很困惑。

根据作者的描述,在 的左大括号处switch,编译器已经为x2and分配了空间x3。如果是这种情况,则有x2可能未初始化(不满足情况 1)。

4

4 回答 4

5

根据我的理解,如果 i 不是 1,x2 可以绕过初始化。

不,要么case 1执行,然后在块x2末尾定义然后销毁switch,要么不执行任何情况,整个switch块什么都不做,所以x2不在范围内,所以它没有被初始化,但它不能被提到任何一个。所以要么它存在并且可以安全使用,要么它不存在。

于 2013-02-20T21:54:03.893 回答
2

C++ 对象初始化和销毁​​语义是完全跳转安全的(包括异常、goto、switch 和循环结构)。

(唯一值得注意的例外是继承自 C 标准库(setjmp/longjmp))

6.6 跳转语句

2从范围退出时(无论如何完成),已在该范围内构建的具有自动存储持续时间 (3.7.3) 的对象将按其构建的相反顺序销毁。[注:对于临时性,见 12.2。—结束注释]转移出循环,转移出块,或回过具有自动存储持续时间的初始化变量涉及销毁具有自动存储持续时间的对象,这些对象在转移点的范围内但不在转移点的范围内. (参见 6.7 中的转移到块)。[ 注意:但是,程序可以终止(例如,通过调用 std::exit() 或 std::abort() (18.5))而不破坏具有自动存储持续时间的类对象。——尾注]

于 2013-02-20T21:52:27.683 回答
0

不,即使isx2也无法绕过。的问题是,如果它被采用,则不会调用构造函数(请记住,构造函数在语句中被“调用” - 因为对构造函数的调用高于跳转目标,您将在仍在范围内的点。i1gotox1X x1;x1

但是你不能跳过这条线X x2;并最终到达仍在范围内的点x2,所以没关系。

于 2013-02-20T21:58:43.467 回答
0

它的问题在于,当您退出其范围时,本地初始化变量会被破坏。c++ 认为 switch,if,while,for 在创建变量方面都是它们自己的范围。

于 2013-02-20T21:56:28.617 回答