全局對象的析構順序與構造順序相反,但構造順序跨編譯單元是未定義的。這導致:
典型場景:WebKit JavaScriptCore 的 VM 對象,必須存活至進程結束,任何提前析構都會導致 JS 引擎失效。
MyClass* g_ptr = new MyClass(); // 永不 delete
缺點:指針可被任意代碼竄改/刪除,破壞封裝;且無法調用構造函數參數。
MyClass g_obj; // 進程結束時仍會隱式調用 ~MyClass()
無法解決析構順序問題。
利用 C++ 保證的「函數靜態局部變量首次調用時初始化,且生命週期至程式結束」特性,配合 new 分配於堆上並故意不釋放:
MyClass& getInstance() {
static MyClass* ptr = new MyClass(args); // 故意洩漏
return *ptr;
}
優點:
deleteDEFINE_STATIC_LOCAL 宏#ifndef DEFINE_STATIC_LOCAL
#if COMPILER(GCC) && defined(__APPLE_CC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 0 && __GNUC_PATCHLEVEL__ == 1
#define DEFINE_STATIC_LOCAL(type, name, arguments) \
static type* name##Ptr = new type arguments; \
type& name = *name##Ptr
#else
#define DEFINE_STATIC_LOCAL(type, name, arguments) \
static type& name = *new type arguments
#endif
#endif
針對舊版 GCC 4.0.1 的編譯器 Bug 做了特化處理。使用示例:
DEFINE_STATIC_LOCAL(VM, vm, ()); // 定義全局 VM 實例 vm
quick_exit 繞過析構C++11 提供 std::quick_exit,直接終止程式不調用全局/靜態對象析構函數(但會調用 at_quick_exit 登記的處理器)。適用於「明確不需要清理」的快速退出場景。
| 方案 | 適用場景 | 風險 |
|---|---|---|
| 函數靜態局部 + new 洩漏 | 單例、引擎核心對象、必須存活至進程結束 | 工具報「記憶體洩漏」(需標註抑制) |
| quick_exit | 無需清理資源的快速退出 | 資源由 OS 回收,適用於短命進程 |
| POD 類型全局變量 | 純數據、無析構邏輯 | 功能受限 |
核心原則:若對象生命週期 = 進程生命週期,就不應有析構函數。通過「延遲初始化 + 故意洩漏」優雅解決 C++ 全局對象析構難題。