我想知道,除了纯粹的好奇心(因为没有人应该像这样编写代码!),关于 RAII 的行为如何使用 goto < / strong>(可爱的主意不是这样)。
class Two
{
public:
~Two()
{
printf("2,");
}
};
class Ghost
{
public:
~Ghost()
{
printf(" BOO! ");
}
};
void foo()
{
{
Two t;
printf("1,");
goto JUMP;
}
Ghost g;
JUMP:
printf("3");
}
int main()
{
foo();
}
在Visual Studio 2005中运行以下代码时,我得到以下输出。
1,2,3 BOO!
然而我想象,猜想,希望'BOO!'实际上不会出现,因为 Ghost 应该从未被实例化(恕我直言,因为我没有知道这段代码的实际预期行为。)
怎么了?
我刚刚意识到,如果我为Ghost实例化一个显式构造函数,代码就无法编译......
class Ghost
{
public:
Ghost()
{
printf(" HAHAHA! ");
}
~Ghost()
{
printf(" BOO! ");
}
};
啊,这个谜......
答案 0 :(得分:24)
标准明确地谈到了这一点 - 举个例子; 6.7 / 3“声明声明”(我强调):
每次执行声明语句时,都会初始化具有自动存储持续时间的变量。 块中声明的具有自动存储持续时间的变量在从块中退出时被销毁。
可以转换为块,但不能以初始化绕过声明的方式。从具有自动存储持续时间的局部变量不在范围内的点跳转到其在范围内的点的程序是不正确的,除非变量具有POD类型并且在没有初始化器的情况下声明。
[实施例:
void f() { //... goto lx; //ill-formed: jump into scope of a //... ly: X a = 1; //... lx: goto ly; //OK, jump implies destructor //call for a, followed by construction //again immediately following label ly }
-end example]
所以在我看来,MSVC的行为不符合标准 - Ghost
不是POD类型,因此编译器应该在goto
语句编码跳过它时发出错误。
我尝试的其他几个编译器(GCC和Digital Mars)发出错误。 Comeau发出警告(但公平地说,我为Comeau构建的脚本配置了高MSVC兼容性,因此它可能会故意跟随微软的领导)。
答案 1 :(得分:0)
Goto不具有放射性。离开goto与异常离开几乎没有什么不同。通过goto输入应该由方便而不是语言的限制来决定。不知道幽灵是否被建造是一个不这样做的好理由。
在构造函数之前跳转。如果您想在已经构造某个对象后跳入,请将其封装在新范围内或以其他方式自行解决其生命周期。
答案 2 :(得分:0)
在这种情况下,我发现以下方法很有用。
void foo()
{
{
Two t;
printf("1,");
goto JUMP;
}
{
Ghost g;
// operations that use g.
}
// g is out of scope, so following JUMP is allowed.
JUMP:
printf("3");
}
在foo()函数中限制变量g的范围,将使goto跳转合法。现在,我们不是从没有初始化g的地方跳到预期初始化g的地方。