使用全局实例销毁 class 中的静态成员

Destruction of static member in a class with global instance

我有一个带有静态非原始成员的 class。例如:

class SomeObject
{
    ... // something that will be destroyed in destructor,
        // like an array pointer.

public:
    SomeObject();
    ~SomeObject(); 
};

class MyClass
{
    static SomeObject m_object;

public:
    MyClass();
    ~MyClass(); // this will access m_object

    static bool SetupStaticMember();
};

/// Implementation of SomeObject and MyClass ///

SomeObject MyClass::m_object;
bool dummy = MyClass::SetupStaticMember(); // Setup the values in m_object, for example,
                                           // allocate memory that will be released in ~SomeObject().

MyClass g_my_global_class;

g_my_global_class 被声明为全局变量,所以它的析构函数在离开 main() 后被调用。 但是MyClass::m_object是静态的,所以在main()之后也会被销毁。

是否可以保证 ~MyClass() 会在 ~SomeObject() 之前从 MyClass::m_object 执行?换句话说,当一个全局class实例的析构函数被调用时,我是否可以假设这个class的静态成员仍然在那里可以访问,或者这取决于construction/destruction订单?

如果按这个顺序写代码,我觉得g_my_global_class是后构造的,应该先析构。如果行

,事情会发生变化吗
MyClass g_my_global_class;

移动到另一个 .cpp 文件并且其文件名导致顺序更改?

首先,

bool dummy = MyClass::InitStaticMember(); // m_object is initialized here

实际上并没有初始化静态成员。这发生在

之前的行
SomeObject MyClass::m_object;

所以既然你基本上有

SomeObject MyClass::m_object;
MyClass g_my_global_class;

并且由于对象以相反的顺序销毁,因此 g_my_global_classMyClass::m_object 之前被销毁。

现在,如果您将 MyClass g_my_global_class; 移动到不同的翻译单元,那么所有的赌注都会取消。仅在单个翻译单元中保证排序。

class 的静态成员是 class 级变量,在 class 范围内。由于静态成员在 class 的范围内,因此只能从使用 :: 从 class 实例化的所有对象访问它(classname::static_variable ) 操作员。由于它不是对象变量,因此您无法从 class.

的析构函数中释放

假设您在代码中的不同位置从 class 创建了十个对象,并且一个对象超出其范围并将调用其析构函数,如果您从该析构函数中释放静态变量,将会发生什么发生在其他物体上?因为它们都共享同一个静态成员。这就是静态成员从未在构造函数中初始化的原因。

因此静态成员只有在程序退出时才会从内存中释放。

Is there any guarantee that ~MyClass() will execute before ~SomeObject() from MyClass::m_object?

是的。静态存储中的对象以相反的初始化顺序销毁。

静态初始化对象如MyClass::m_objectg_my_global_class按定义顺序初始化。所以先定义的MyClass::m_object也是最先初始化,最后销毁的

Do things change if the line ... moves to another .cpp file and its file name causes the order to change?

是的,情况发生了变化。跨翻译单元的定义顺序未指定,因此无法保证相对初始化顺序(因此无法保证相对销毁顺序)。

静态对象之间依赖关系的典型解决方案是使用 首次使用时初始化 惯用语,它只是用 [=35] 的函数替换全局静态的使用=] 对本地静态的引用:

class MyClass
{
    static SomeObject& m_object() {
        static SomeObject s;
        return s;
    }
};

局部静态对象在执行第一次到达声明点时被初始化;因此成语的名称。

任何其自己的初始化调用 MyClass::m_object() 的对象都保证在局部静态 s 被销毁之前被销毁,因此可以在整个生命周期中依赖它的存在 - 包括析构函数,无论翻译单元边界。

如果您使用的是 visual studio - windows,请检查一下:否则您可能必须使用类似的东西,例如 __PRETTY_FUNCITON__

class SomeObject {
public:
    SomeObject() {
        std::cout << __FUNCTION__ << " was called: SomeObject created." << std::endl;
    }
    ~SomeObject() {
        std::cout << __FUNCTION__ << " was called: SomeObject destroyed." << std::endl;
    }
};

class MyClass {
public:
    static SomeObject m_object;

    MyClass() {
        std::cout << __FUNCTION__ << " was called: MyClass created." << std::endl;
    }
    ~MyClass() {
        std::cout << __FUNCTION__ << " was called: MyClass destroyed." << std::endl;
    }

    static bool setupStaticMember() {
        std::cout << __FUNCTION__ << " was called... " << std::endl;
        return true;
    }

};

SomeObject MyClass::m_object;
bool dummy = MyClass::setupStaticMember();
MyClass gMyClass;

int main() {



    _getch();
    return 0;
}

等待按键时调试器控制台的输出:

SomeObject::SomeObject was called: SomeObject created.
MyClass::setupStaticMember was called...
MyClass::MyClass was called: MyClass created.

然后随着按键的输入和调试器的控制台关闭(visual studio 2017)...

MyClass::~MyClass was called: MyClass destroyed.
SomeObject::~SomeObject was called: SomeObject destroyed.

要对此进行全面测试,只需直接转到控制台中的 *.exe 路径并调用可执行文件即可。当应用程序处于 运行 时,您将看到上面相同的行,但在您按下一个键并输入以完成应用程序后,最后两行将按该顺序调用。

这些都在main.cpp文件中(相同的翻译单元)。

这确实显示了创建对象的顺序。现在,如果您处于 class 层次结构中,那么您需要确保虚拟化您的 classes 以实现正确的构建 - 销毁顺序。