在C ++中为临时对象调用析构函数的顺序是什么?

时间:2012-12-11 15:50:40

标签: c++ destructor temporary object-lifetime

请考虑以下代码:

#include <iostream>

struct A {
  ~A() { std::cout << "~A" << std::endl; }
};

struct B {
  ~B() { std::cout << "~B" << std::endl; }
};

struct C {
  ~C() { std::cout << "~C" << std::endl; }

  void operator<<(const B &) {}
};

C f(const A &a = A()) {
  return C();
}

int main() {
  f(A()) << B();
}

使用GCC进行编译并运行会提供以下输出:

~C
~A
~B

是否可以保证在与其他编译器一起编译时,将按此顺序调用类型A,B和C的临时对象的析构函数?一般来说,如果有的话,析构函数调用的顺序是什么?

3 个答案:

答案 0 :(得分:10)

让我们谈谈子表达式及其排序。如果E1E2之前排序,则表示必须在E1之前对E2进行全面评估。如果E1 未按 E2排序,则表示可以按任何顺序评估E1E2

对于f(A()) << B(),在您的情况下与f(A()).operator<<(B())相同,我们知道:

  • A()f(...)
  • 之前排序
  • f(...)operator<<
  • 之前排序
  • B()operator<<
  • 之前排序

这也告诉我们:

  • A()operator<<
  • 之前排序 {li> A()未按B() 排序 {li> f(...)未按B() 排序

如果我们假设RVO,为了不使事情复杂化,编译器可以评估子表达式的可能顺序是:

  • A() - &gt; f(...) - &gt; B(),产生~B() - &gt; ~C() - &gt; ~A()
  • A() - &gt; B() - &gt; f(...),产生~C() - &gt; ~B() - &gt; ~A()
  • B() - &gt; A() - &gt; f(...),产生~C() - &gt; ~A() - &gt; ~B()

后者是在OP中观察到的顺序。请注意,破坏的顺序始终与构造的顺序相反。

答案 1 :(得分:3)

未指定表达式f(A()) << B();的评估顺序。因此,也没有规定构造/破坏的顺序。

答案 2 :(得分:2)

<<的操作数的评估顺序未指定。因此,订单无法保证。只有短路运算符&&||,三元运算符?:和逗号,运算符具有明确定义的操作数评估顺序。对于其他人,不必在右操作数之前评估左操作数(反之亦然)。

此外,不要将运算符优先级或关联性与评估顺序混淆。对于给定的表达式E1 op E2,只需要在应用运算符op之前,应评估E1E2,但在它们之间,{{1} }和E1可以按任何顺序进行评估。

优先级规则决定在表达式中有多个运算符时应用运算符的顺序,例如E2

关联性用于决定哪个操作数绑定到哪个运算符,当同一个运算符被多次使用时,即在E1 op1 E2 op2 E3中,是否被解释为E1 op E2 op E3或{{1} }。