2010-03-09 40 views
12

我想知道,没有其它目的不是纯粹的好奇心(因为任何人都不应该这样写代码!)关于RAII的行为如何与使用转到金属网格(可爱的想法是不是) 。当我们结合RAII和GOTO时会发生什么?

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应该从未实例化过(恕我直言,因为我不知道此代码的实际预期行为)。

这是怎么回事?


我意识到,如果我实例化鬼的代码不编译一个明确的构造函数...

class Ghost 
{ 
public: 
    Ghost() 
    { 
     printf(" HAHAHA! "); 
    } 
    ~Ghost() 
    { 
     printf(" BOO! "); 
    } 
}; 

啊,神秘......

+1

我相信这种行为是正确的。否则,如何在JUMP之后引用变量g? – leiz 2010-03-09 05:00:50

+2

http://xkcd.com/292/ – 2010-03-09 05:21:19

回答

23

有关标准会谈这明确地 - 用一个例子; 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 
} 

末端示例]

所以,在我看来,MSVC的行为是不符合标准 - Ghost不是一个POD类型,所以编译器应该发出当goto语句被编码跳过它时出错。

我尝试过的一些其他编译器(GCC和Digital Mars)发布错误。 Comeau发出警告(但公平地说,我为Comeau构建的脚本配置了高MSVC兼容性,因此它可能会故意追踪微软的主管)。

+1

感谢您找到标准中定义的位置!然而,我不知道如果形成不良意味着它应该或不应该编译... – 2010-03-09 05:28:53

+0

“不良形成”意味着该程序不是“良构”。编译器只需接受和“正确执行”格式良好的程序。也就是说,如果它“不合格”,那就是错误的。 – greyfade 2010-03-09 05:43:40

+0

@Robert:我在这里添加了一些关于MSVC行为的话,因为我最初应该这样做。 – 2010-03-09 09:20:30

0

转到没有放射性。由goto离开与离开异常毫无区别。通过goto进入应该是方便的,而不是语言的限制。不知道鬼是否构造是一个不这样做的好理由。

在构造函数之前跳转。如果您想在某个对象已经构建之后跳入,请将其放入新范围或以其他方式自行解析其生命周期。

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"); 
} 

将变量g的范围限制在foo()函数中,会使跳转合法。现在,我们不是从g未初始化的地方跳到g预期初始化的地方。

相关问题