使用全局实例破坏类中的静态成员

问题描述 投票:7回答:4

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

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()会在MyClass :: m_object之前的〜SomeObject()之前执行?换句话说,当调用全局类实例的析构函数时,我可以假设此类的静态成员仍然存在,或者这取决于构造/销毁订单吗?

如果代码按此顺序编写,我认为g_my_global_class是稍后构造的,因此应首先对其进行破坏。如果线路做的事情会改变

MyClass g_my_global_class;

移动到另一个.cpp文件,其文件名导致订单更改?

c++ static destructor member
4个回答
5
投票

第一,

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;移动到另一个翻译单元,那么所有的赌注都会被取消。订购仅在单个翻译单元中保证。


2
投票

有没有保证~MyClass()将在~SomeObject()MyClass::m_object之前执行?

是。静态存储中的对象与初始化的逆序相反。

静态初始化对象(如MyClass::m_objectg_my_global_class)按定义的顺序初始化。因此,首先定义的MyClass::m_object也首先被初始化,并且最后被破坏。

如果行...移动到另一个.cpp文件并且其文件名导致订单改变,那么事情会改变吗?

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

静态对象之间依赖关系的典型解决方案是在首次使用的习惯用法上使用初始化,这只是将全局静态的使用替换为返回对本地静态的引用的函数:

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

当执行首次到达声明点时,初始化本地静态对象;因此成语的名称。

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


1
投票

如果您使用的是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文件中(相同的翻译单元)。

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


0
投票

类的静态成员是类级变量,在类范围下。由于静态成员仅在类的范围内,因此可以使用::(classname :: static_variable)运算符从该类实例化的所有对象访问它。因为它不是对象变量,所以无法从类的析构函数中释放。

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

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

© www.soinside.com 2019 - 2024. All rights reserved.