16

我想知道,纯粹出于好奇(因为没有人应该编写这样的代码!)关于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不应该被实例化(恕我直言,因为我不知道这段代码的实际预期行为)。

这是怎么回事?


我刚刚意识到,如果我为 Ghost 实例化一个显式构造函数,则代码不会编译......

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

啊,奥秘……

4

3 回答 3

25

该标准明确地讨论了这一点 - 举个例子;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 的行为不符合标准 -不是 POD 类型,因此当语句被编码以跳过它Ghost时,编译器应该发出错误。goto

我尝试过的其他几个编译器(GCC 和 Digital Mars)发出错误。Comeau 发出警告(但公平地说,我的 Comeau 构建脚本已将其配置为具有高 MSVC 兼容性,因此它可能是有意效仿 Microsoft 的)。

于 2010-03-09T05:10:58.967 回答
0

Goto 没有放射性。通过 goto 离开与通过异常离开几乎没有区别。通过 goto 输入应该是为了方便,而不是语言的限制。不知道幽灵是否被构建是不这样做的一个很好的理由。

在构造函数之前跳转。如果您想在某个对象已经构建后跳入,请将其包含在一个新的范围内,或者自己解决它的生命周期。

于 2010-03-09T05:37:34.640 回答
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() 函数中,将使 goto 跳转合法。现在,我们不会从 g 未初始化的地方跳到 g 预期被初始化的地方。

于 2017-01-05T02:30:35.493 回答