【问题标题】:Why is the order of destruction same as order of construction, with static object (C++)?为什么破坏顺序与构造顺序相同,使用静态对象(C++)?
【发布时间】:2021-04-20 05:27:15
【问题描述】:

代码是:

#include <iostream>
using namespace std;

class A
{
public:
    A() { cout << "A::A" << endl; }
    ~A() { cout << "A::~" << endl; }
};

class B
{
public:
    B() { cout << "B::B" << endl; }
    ~B() { cout << "B::~" << endl; }
};

int main()
{
    B b;
    static A a;
    return 0;
}

输出是:

B::B
A::A
B::~
A::~

非静态对象b的范围和静态对象a的范围在main()函数的末尾结束。

问:为什么构造函数的顺序和析构函数的顺序一样?

【问题讨论】:

    标签: c++ constructor static destructor


    【解决方案1】:

    Static local variables 将在程序退出时销毁。

    块范围静态变量的析构函数在程序退出时调用,但前提是初始化成功。

    所以b会在main()的末尾先被销毁,a之后会被销毁。

    对于初始化,

    在控件第一次通过其声明时被初始化

    所以b会首先在main()中初始化,然后a被初始化。

    【讨论】:

    • main()的结尾是否与程序退出相同??
    • @KrishnaKanthYenumula 不,main() 结束后,还有一些事情需要做,比如销毁具有静态存储持续时间的对象,包括这里的a
    • @KrishnaKanthYenumula 不,不是真的。实现已定义,但 main 在运行时执行某些操作之前进入和退出方式。您首先获得库和全局变量 intializatin,然后在 main() 之后,您会以某种复杂(且未完全定义)的顺序销毁所有内容。
    • @KrishnaKanthYenumula 当main()结束时,只有在main()中定义的具有自动存储期限的对象才会被销毁,包括b这里。
    【解决方案2】:

    因为它们的寿命不同。 A 被声明为函数局部静态变量。

    由自动函数局部变量声明创建的对象具有生命周期,该生命周期在对该对象的任何使用之前开始,并以包含该声明的最嵌套代码块(大括号块)结束。在你的情况下,这是函数main() body。

    通过声明静态函数局部变量创建的对象在执行流程进入包含该声明的最嵌套代码块之后并且在对该对象进行任何使用之前开始存在。 它具有进程范围的生命周期(停止存在于std::atexit()),这发生在函数main() 将退出之后。

    所以它们是在这种特殊情况下按声明顺序创建的,但 A 稍后会被销毁。如果你的函数被调用了两次,你会看到B 会被创建两次,但A 只会被创建一次。如果函数的流程会以某种方式通过if() 语句或提前返回而省略任何声明,则它们的创建顺序会改变或两者都可能被省略。

    它是特定于实现的细节,但通常以与销毁全局对象相同的方式实现函数局部静态的破坏,方法是调用位于std::atexit 实现下的库函数,添加与值绑定的析构函数的地址指向对象本身的指针,但执行可能与用户调用 std::atexit 的结果并发(或不并发)。

    【讨论】:

      【解决方案3】:

      作为现有答案的补充,让我们采用更现象学的方法。考虑对您的示例进行一些小改动:

      void foo() {
          B b;
          static A a;
      }
      
      int main() {
          foo();
          foo();
      }
      

      我假设你知道a 只被初始化一次,在第二次调用中我们得到完全相同的对象a,因为它被声明为static。因此,它不能在第一次调用foo 期间或之后立即销毁。预期输出的第一部分是

      B::B    // first call to foo()
      A::A
      B::~
      B::B    // second call to foo()
              // no second call to A::A !
      B::~    
      

      a 只能在程序终止时销毁,否则在再次调用函数时无法“重用”它(这就是我不得不修改示例的原因,您不能调用main)。正如其他答案详细解释的那样,这发生在main 返回之后。因此最后一行输出将是

       A::~
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-06-12
        • 2012-12-15
        • 2010-10-26
        • 1970-01-01
        • 1970-01-01
        • 2012-12-31
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多