【问题标题】:Why do un-named C++ objects destruct before the scope block ends?为什么未命名的 C++ 对象会在作用域块结束之前破坏?
【发布时间】:2011-01-18 22:03:11
【问题描述】:

以下代码打印一、二、三。这对所有 C++ 编译器来说都是期望的和真实的吗?


class Foo
{
      const char* m_name;
public:
      Foo(const char* name) : m_name(name) {}
      ~Foo() { printf("%s\n", m_name); }
};

void main()
{
      Foo foo("three");
      Foo("one");   // un-named object
      printf("two\n");
}

【问题讨论】:

标签: c++ scope destructor


【解决方案1】:

一个临时变量一直存在到创建它的完整表达式的末尾。你的以分号结尾。

这是在 12.2/3 中:

临时对象在评估完整表达式 (1.9) 的最后一步时被销毁,该完整表达式 (从词法上) 包含它们的创建点。

你的行为是有保证的。

有两个条件,如果满足,将延长临时的生命周期。第一个是当它是一个对象的初始化器时。第二个是引用绑定到临时的。

【讨论】:

  • ... 除非满足 12.2/4 的条件,否则可以进一步延长临时对象的生命周期。
  • @AndreyT:嗯,是的。我想这可能很重要。
  • 在原始问题的上下文中提及可能并不重要。但是在阅读了您的明确断言后,说“表达式结束时临时变量的生命周期结束”,我认为这是值得一提的。
  • @Andrey:我同意,我原来的帖子让它看起来是无条件的。
【解决方案2】:

管理临时对象生命周期的规则与 范围 的概念无关。范围是 name 的属性,临时对象没有名称。换句话说,临时对象没有作用域。

大多数情况下,临时对象的生命周期在创建该对象的完整表达式结束时结束,这就是您在实验中观察到的情况。这是一般规则,但也有一些例外。主要的一点是,如果您立即将引用附加到临时对象,则对象的生命周期将延长以匹配引用的生命周期

const Foo &rfoo = Foo("one");

上述临时文件将与rfoo 一样长。

【讨论】:

  • +1 Scope is a property of a name, and temporary objects do not have names. 临时扩展绑定到没有const 限定符的引用的生命周期? Foo &rfoo = Foo("one") ;
  • 我读到临时的生命周期只能扩展绑定到const 引用。但是示例 ideone.com/nTVPZ 在 VS 2010 上运行良好,但在 gcc 上运行良好(在这两种情况下,this 在给定的链接中也是相同的,表示即使在 VS 上没有 const 限定符,也可以临时延长寿命)。这里 gcc 或 VS 正确吗?
  • @Mahesh:代码在标准 C++ 中不可编译,这就是 GCC 拒绝它的原因。 VS 仅因为您启用了扩展而接受它作为扩展。如果在 VS 中禁用语言扩展,代码也不会在 VS 中编译。
【解决方案3】:

像这样的临时对象的范围只有一行。想一想,行结束后就不能再引用它了,那这个对象怎么还留着呢?

如果不是这样,编译器将无法优化函数调用中的临时对象。

【讨论】:

    【解决方案4】:

    是的,这是需要的。

    Foo foo("three") 创建一个普通对象,该对象将在作用域结束时被销毁。

    Foo("one") 创建一个临时对象,该对象在指令结束时被销毁[1]。为什么?因为指令结束后就无法访问了。

    [1] 刻意简化:我应该说序列点

    【讨论】:

    • 序列点与此无关。临时的生命周期在完整表达式的末尾结束,即使在创建对象的“指令”末尾有一个序列点。例如,在Foo("a"), Foo("b"), Foo("c"); 中,所有三个临时对象都保证存活到语句的末尾,即使中间有序列点。
    【解决方案5】:

    因为标准委员会搞砸了。它这样做是因为他们选择让它这样做。它被定义为这样做。它应该被视为一个匿名实例,其范围与它的命名相同。从实例化点到块结束。显然他们认为唯一的用途是将临时变量传递给函数,然后在函数调用结束时将其压入堆栈并从堆栈中弹出...

    一个未命名的对象仍应被压入堆栈并保留在堆栈中直到块结束,从而在预期时将其从堆栈中弹出。在单个语句中构造和销毁对象是没有意义的。我希望看到一个实际有用的实例/案例。如果它在块的持续时间内没有停留在范围内,那么它肯定是一个错误,并且至少应该生成一个警告。

    【讨论】:

    • 没有必要明确谈论堆栈;完全有可能拥有一个完全不使用堆栈的可用 C++ 实现,而是在静态位置分配所有内容。小的临时值也可以在寄存器中传递,而不是在可寻址内存中。
    • “在单个语句中构造和销毁对象是没有意义的。”如果你这样做是为了副作用,或者在转换链中。它非常有用,像 Firefox 这样的主要 C++ 项目可能到处都有数千个这样的结构。它们非常有用。如果这些对象一直保留到块的末尾,那就相当糟糕了,缓存压力会扼杀许多项目的性能,即使整体内存使用不会发生显着变化。
    猜你喜欢
    • 2019-08-05
    • 1970-01-01
    • 2021-08-03
    • 1970-01-01
    • 1970-01-01
    • 2016-09-09
    • 1970-01-01
    • 1970-01-01
    • 2021-08-04
    相关资源
    最近更新 更多