对于静态对象(C++),为什么破坏的顺序与构建的顺序相同



代码为:

#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()函数的末尾。

问题:为什么构造函数的顺序和析构函数的顺序相同?

静态局部变量将在程序退出时销毁。

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

因此,b将在main()结束时首先销毁,a将在那之后销毁。

对于初始化,

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

因此b将首先在main()中初始化,然后a将被初始化。

因为他们有不同的寿命。CCD_ 10被声明为函数局部静态变量。

通过声明自动函数局部变量创建的对象的寿命在使用该对象之前开始,并以包含该声明的嵌套最多的代码块(大括号块)结束。在您的情况下,这是函数main()体。

由静态函数局部变量声明创建的对象在执行流进入包含该声明的嵌套最多的代码块之后并且在使用该对象之前就开始存在。它获得了整个进程的寿命(在std::atexit()停止存在),这发生在函数main()将退出之后。

因此,在这种特殊情况下,它们是按照声明的顺序创建的,但A稍后会被销毁。如果您的函数被调用两次,您会看到B将被创建两次,而A仅被创建一次。如果函数的流以某种方式通过if()语句或提前返回省略了其中一个声明,则它们的创建顺序将发生变化,或者两者都可以省略。

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

除了已有的答案之外,让我们采取一种更现象学的方法。在你的例子上考虑一个小的变化:

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::~

最新更新