使用全局实例销毁 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_class
在 MyClass::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_object
和g_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 以实现正确的构建 - 销毁顺序。
我有一个带有静态非原始成员的 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_class
在 MyClass::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()
fromMyClass::m_object
?
是的。静态存储中的对象以相反的初始化顺序销毁。
静态初始化对象如MyClass::m_object
和g_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 以实现正确的构建 - 销毁顺序。