【问题标题】:Goto out of a block: do destructors get called?Goto out of a block:析构函数会被调用吗?
【发布时间】:2011-03-11 22:21:21
【问题描述】:

考虑以下代码:

void foo()
{
    {
        CSomeClass bar;

        // Some code here...

        goto label;

        // and here...
    }

label:
    // and here...
}

会调用bar的析构函数吗?

【问题讨论】:

  • 为什么你需要知道这个? goto 在生成的代码中是有意义的,但无论如何你可以而且应该避免使用带有析构函数的对象。
  • @MSalters:这根本没有意义,为什么要避免使用带有析构函数的对象?
  • @Viktor:让我扩展句子的第二部分:在生成的代码中,无论如何,你可以而且应该避免使用析构函数的对象。 goto 的规范用法是在生成的 FSM 代码中。这里发生了前后跳转,而不考虑恰好位于中间的状态代码状态。 FSM 状态根本不是线性的,但 C++ 代码必须是。
  • 需要注意的是breakcontinue有一个非常相似的效果,并不是说和goto完全一样,只是标签是由编译器在正确的位置自动生成。然而,for() 循环中的break 预计会正确,goto 也应该如此。

标签: c++ destructor goto


【解决方案1】:

C++ 标准说:

在退出范围时(但是 完成),析构函数(12.4)是 调用所有构造的对象 具有自动存储期限 (3.7.2) (命名对象或临时对象) 在该范围内声明的,在 他们的相反顺序 声明。

所以答案是“是”。

【讨论】:

  • 谢谢。这正是我所需要的。
【解决方案2】:

是的,他们会被召唤。

更新: (这样做没关系,goto 并不比抛出虚拟异常或使用 bools/ifs 摆脱事物更糟糕。函数内部的简单 goto 不会使其成为意大利面条代码。)

【讨论】:

  • +1 否定自负的学生,因为他们个人认为 goto 是邪恶的,所以他们觉得有必要投反对票。无论您的教授告诉您什么,Goto 都有有效的用途。
  • "这样做没关系,gotos 并不比抛出 dummy 异常或使用 bools/ifs 摆脱事情更糟糕" 抛出 dummy 异常是一种糟糕的方法(抛出异常通常是非常昂贵),如果它介于两者之间,我肯定更喜欢 gotos。但是,为这些情况编写函数无疑是最好的方法。
  • @KevinK - Gotos 在理论上可能有有效的用途,但在实践中却极为罕见。我会争辩说,如果看起来你必须使用 goto,那么这种情况至少值得仔细检查。对于初学者来说,我建议避免使用它们是一个很好的学习纪律。
  • @jwismar 如果 goto 有助于提高代码的可读性,我建议继续使用它。一个合法使用的例子是从嵌套循环中逃脱。
  • 我在this answer讨论了goto。
【解决方案3】:

1) 是的。 2) 不要这样做。

细化:从概念上讲,这与通过break 离开循环没有什么不同。然而,goto 强烈、强烈地劝阻。几乎从不需要使用goto,任何使用都应仔细检查以了解发生了什么。

【讨论】:

  • 2) 当你实际转义循环时,你会这样做。
  • 实际上,goto 在很多情况下都是有意义的。
  • 休息会更好。至少我知道我不会往上跳。
  • +1 对于原始问题,函数可能是最好的选择。
  • gotobreakcontinue 最糟糕的是,在块末尾的“返回”并不意味着“控制无法超出此范围”。出于这个原因,我总是尽量避免这三个。我个人认为breakgoto 之间没有太大区别。当然goto 的情况更糟一些,但并没有增加太多。所有这些都有其用途,但所有这些都使代码复杂化
【解决方案4】:

是的,正如其他人所说。 C++ 指定/强制执行此操作。

但只是为了完整性:如果您的 goto 使用在某些编译器中找到的计算的-goto 扩展 - gcc、clang,可能还有其他,但 最后包括 MSVC我知道——对象的析构函数是否会被调用是非常模糊的。当goto 转到单个位置时,在控制流传输之前必须调用哪些析构函数非常清楚。但是对于计算出的goto,可能需要动态调用不同的析构函数,以提供“预期的”语义。在这些情况下,我不确定实现此扩展的编译器会做什么。我遇到这种情况的记忆是,当计算的-goto 可能会留下带有非平凡析构函数的对象的作用域时,clang 会发出警告,声称不会调用析构函数。在某些情况下可能很好,在其他情况下则不然。我不知道其他编译器做了什么。如果您想将计算的gotos 与具有非平凡析构函数的对象一起使用,请注意这个问题。

【讨论】:

  • 谢谢。我只使用 C 中的计算 goto,而且在非常罕见的情况下。
  • 对于那些想知道什么是计算 goto 的人,请参阅 herehere
【解决方案5】:

虽然析构函数意味着要被调用,但请注意gcc有一个bug

如果您的代码具有以下形式,您在其中返回类并执行应该导致其被破坏的跳转,则将跳过析构函数。

CSomeClass foo()
{
    label:
    CSomeClass bar;

    // Some code here...

    if (someCondition)
       goto label; // if this jump is taken, bar is not destructed in gcc

    return bar;
}

【讨论】:

    猜你喜欢
    • 2010-09-24
    • 2012-09-02
    • 1970-01-01
    • 1970-01-01
    • 2020-08-22
    • 2011-01-31
    • 2013-08-31
    相关资源
    最近更新 更多