【问题标题】:Destruction of static member in a class with global instance销毁具有全局实例的类中的静态成员
【发布时间】:2018-08-13 06:10:57
【问题描述】:

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

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


【解决方案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 文件中(同一个翻译单元)。

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

【讨论】:

    【解决方案2】:

    是否保证~MyClass() 会在~SomeObject() 之前从MyClass::m_object 执行?

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

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

    如果行 ... 移动到另一个 .cpp 文件并且其文件名导致顺序改变,情况会改变吗?

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

    静态对象之间依赖关系的典型解决方案是使用 initialization on first use 习惯用法,它只是将全局静态对象的使用替换为返回对本地静态对象的引用的函数:

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

    本地静态对象在执行第一次到达声明点时被初始化;因此成语得名。

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

    【讨论】:

      【解决方案3】:

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

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

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

      【讨论】:

      • 我的问题是一个带有一些静态成员的类也被用作全局变量,所以它的销毁也会在程序退出时发生。如果该类用作 main() 中的局部变量,那将是安全的,但如果它必须是全局的(例如,防止多个实例),我想知道我应该怎么做才能保证安全。
      【解决方案4】:

      首先,

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

      【讨论】:

      • 谢谢,但我想先确认一下。我知道 SomeObject 的构造函数只是在定义 MyClass::m_object 时被调用,并且术语“初始化”意味着在构造函数之后设置成员值(或可能分配内存)。我适合这部分吗?很抱歉造成混乱。
      • @Mark 您在定义变量时对其进行了初始化。如果您不将其初始化为任何内容,则取决于具体情况,它将为您初始化(包括使其处于不确定状态)。
      • 我明白了。该变量仍由某个(或未定义的)值“初始化”。我将修改问题以使其更清楚。静态bool函数用来显示我改变了对象中的某些东西,破坏后可能会造成麻烦。
      猜你喜欢
      • 1970-01-01
      • 2012-09-11
      • 1970-01-01
      • 1970-01-01
      • 2011-06-03
      • 2012-12-15
      • 2010-10-17
      • 1970-01-01
      相关资源
      最近更新 更多