C++中Phoenix Singleton如何维持同一内存地址?技术疑问求解
Phoenix Singleton 核心逻辑解惑
这是个非常犀利的问题,刚好抓住了Phoenix Singleton模式最容易让人混淆的核心细节!咱们一步步拆解清楚整个流程:
先解决你的核心疑问:静态对象内存真的被销毁了吗?
首先要明确一个关键知识点:局部静态变量(比如Create()里的theInstance)的内存是分配在静态存储区的,这块内存的生命周期是从程序启动后第一次初始化变量开始,直到整个进程终止才会被操作系统回收。
当程序进入退出序列时,theInstance的析构函数会被调用——但析构函数只是销毁了对象的内部状态(比如释放成员资源、重置类的静态指针pInstance_为0、标记destroyed_为true),它所在的那块静态内存本身并没有被释放,地址依然是有效的、可被访问的。这是整个Phoenix Singleton能“死而复生”的基础!
完整流程拆解
咱们跟着代码走一遍典型的循环场景:
第一次调用
Instance():pInstance_为空,且destroyed_为false,调用Create()Create()里的static Singleton theInstance;初始化局部静态对象,pInstance_被赋值为该对象的地址- 程序正常运行期间,所有
Instance()调用都直接返回这个有效对象的引用
程序进入第一次退出序列:
- 全局/静态对象的析构函数被依次调用,
theInstance的析构函数执行:virtual ~Singleton() { pInstance_ = 0; destroyed_ = true; } - 此时
pInstance_被置空,destroyed_标记为true,但theInstance所在的静态内存依然存在
- 全局/静态对象的析构函数被依次调用,
某个全局对象的析构函数再次调用
Instance():Instance()检测到pInstance_为空且destroyed_为true,调用OnDeadReference()OnDeadReference()里的关键操作:- 调用
Create():因为theInstance是局部静态变量,第二次进入Create()不会重新初始化对象,只会把pInstance_重新指向原来那块静态内存的地址(此时内存里是已经析构的对象“残骸”) - 使用placement new:
new(pInstance_) Singleton;——这个语法的作用是在已经分配好的内存地址上直接构造新对象,完全不需要重新申请内存。因为那块静态内存还在,所以这一步是合法且安全的 - 调用
atexit(KillPhoenixSingleton);:把新的销毁函数加入退出队列,确保这个“复活”的对象在后续退出序列中能被正确处理 - 重置
destroyed_ = false,标记对象重新有效
- 调用
Instance()最终返回复活后的对象引用
程序进入第二次退出序列:
atexit注册的KillPhoenixSingleton()被调用:void Singleton::KillPhoenixSingleton() { pInstance_->~Singleton(); }- 手动调用对象的析构函数,再次触发
pInstance_ = 0和destroyed_ = true - 如果还有全局对象再次访问
Instance(),整个循环会重复,直到没有访问为止
原代码与书中引用整理
Phoenix Singleton 实现代码
Singleton& Instance() { if (!pInstance_) { // Check for dead reference if (destroyed_) { OnDeadReference(); } else { // First call—initialize Create(); } } return *pInstance_; } void Singleton::OnDeadReference() { // Obtain the shell of the destroyed singleton Create(); // Now pInstance_ points to the "ashes" of the singleton // - the raw memory that the singleton was seated in. // Create a new singleton at that address new(pInstance_) Singleton; // Queue this new object's destruction atexit(KillPhoenixSingleton); // Reset destroyed_ because we're back in business destroyed_ = false; } static void Create() { // Task: initialize pInstance_ static Singleton theInstance; pInstance_ = &theInstance; } void Singleton::KillPhoenixSingleton() { // Make all ashes again // - call the destructor by hand. // It will set pInstance_ to zero and destroyed_ to true pInstance_->~Singleton(); } virtual ~Singleton() { pInstance_ = 0; destroyed_ = true; } Singleton* Singleton::pInstance_ = 0; bool Singleton::destroyed_ = false;
书中原文引用
让我们分析事件流程:在应用退出序列中,Singleton的析构函数被调用。析构函数将指针重置为0,并将destroyed_设为true。现在假设某个全局对象再次尝试访问Singleton。Instance函数会调用OnDeadReference。OnDeadReference会重新激活Singleton,并将KillPhoenixSingleton的调用加入退出队列,Instance成功返回指向有效Singleton对象的引用。此后该循环可能重复。
内容的提问来源于stack exchange,提问作者Eduard Rostomyan
相关产品推荐
相关产品推荐

