代码之家  ›  专栏  ›  技术社区  ›  Robert Gould

当我们合并RAII和GOTO时会发生什么?

  •  13
  • Robert Gould  · 技术社区  · 15 年前

    我在想,除了纯粹的好奇,没有别的目的(因为没有人应该写这样的代码!)关于 使用 转到

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

    在visualstudio2005中运行以下代码时,我得到以下输出。

    1,2,3 BOO!
    

    然而我想象,猜测,希望 鬼魂

    怎么了?


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

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

    3 回复  |  直到 13 年前
        1
  •  25
  •   Michael Burr    15 年前

    标准对此进行了明确的讨论,并举例说明;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和digitalmars)出现了错误。Comeau发出了一个警告(但公平地说,我为Comeau编写的构建脚本配置了MSVC兼容性,所以它可能有意跟随微软的步伐)。

        2
  •  0
  •   Potatoswatter    15 年前

    Goto没有放射性。走了就走和例外走没什么区别。通过goto进入应该是出于方便,而不是语言的限制。不知道鬼魂是不是被造出来的是一个很好的理由不这样做。

    在构造器之前跳进去。如果您想在某个对象已经被构造之后跳入,请将它包含在一个新的作用域中,或者自己解析它的生存期。

        3
  •  0
  •   Punit Soni    8 年前

    在这个场景中,我发现以下方法很有用。

    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应该初始化的地方。