單例模式的陷阱

公司編輯UI有個編輯器,是個MFC框架的窗口。長時間以來,每次點關閉按鈕關閉的時候都會報windows的調試,從而再啓動一次。ios

平時你們工做都是趕任務,這個問題一直能忍則忍。後來這個問題致使我其餘vs解決方案連接的時候總是失敗。c++

因爲這個編輯器會引用到另外一個項目的lib,若是編輯器不能正常關閉,那麼另外一個項目連接的時候就會報沒法打開某個lib。windows

因而我就想着怎麼解決編輯器不能正常關閉的問題。框架

編輯器有源碼,是MFC框架的。因而我首先簡單找了一本侯捷的《深刻淺出MFC》看了一下MFC的關閉流程。發現沒有什麼特別的地方。編輯器

爲了保持環境乾淨,我從新clone了一份編輯器的代碼,再從新生成。偶然發現每次關閉以後,工做目錄下都會有個文件的修改時間變成新的。函數

難道有內存泄漏?並且咱們的代碼裏有內存追蹤模塊?spa

打開文件看了以後,確認應該是有內存泄漏。並且每次幾乎是同樣的,泄漏的地方相同。這可能就好找了。3d

不過我好奇怎麼作的內存追蹤,因而我讀了一下咱們引擎裏的內存追蹤模塊。這塊另寫一篇來說。指針

這裏講一個單例模式的內存泄漏。還有怎麼寫能不內存泄漏。調試

直接看一下簡化後的代碼:

 1 class Singleton
 2 {
 3 public:
 4     static Singleton* Instance();
 5     Singleton() = default;
 6     ~Singleton() = delete;
 7 
 8     int mem = 1;
 9 
10     void printInstance() { cout << "print Instance" << endl; }
11 };
12 
13 Singleton * Singleton::Instance()
14 {
15     static Singleton* instance = new Singleton;
16     return instance;
17 }
1 int main()
2 {
3     Singleton::Instance()->printInstance();
4     Singleton::Instance()->printInstance();
5 
6     return 0;
7 }

 運行結果:

注意到幾點:1.析構函數=delete。2.Instance()裏有一個static的Singleton*,有new操做,可是沒有對應的delete操做。

1.析構函數=delete。

這個c++11的新寫法。表明該函數爲刪除函數,也就是不能重載,不會被調用。這類函數能夠聲明,可是沒有定義。編譯器會阻止對它們進行定義。

相似的若是想要阻止拷貝,阻止賦值拷貝,也能夠把拷貝構造函數和賦值拷貝構造函數聲明爲刪除函數。

Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;

可是這裏把析構函數聲明爲刪除函數??這樣就阻止了對象的析構了。
在《c++ primer》第五版的13.1.5節裏有提到,析構函數不能是刪除函數。

這裏有什麼其餘的考量?不得而知。

2.static的Singleton*。

這裏是一個已初始化局部的static變量,咱們知道已初始話的static變量會放在.data區,並且會在程序結束的時候自動清理。

可是這裏是new出來的對象,因此程序結束的時候是不會清理的。

 

怎麼改比較好呢?

1.首先析構函數固然不能是刪除函數。

2.new出來的static對象指針在程序結束不會自動清理,可是static對象會自動析構。能夠利用這一點寫一個static自動清理的類對象。

 1 #include <iostream>
 2 
 3 using namespace std;
 4 
 5 class Singleton
 6 {
 7 public:
 8     Singleton() { cout << "Singleton Constructor " << endl; }
 9     ~Singleton() { cout << "Singleton Destructor " << endl; }
10 
11     static Singleton* Instance();
12     static Singleton* m_instance;
13 
14     int mem = 1;
15 
16     void printInstance() { cout << "print Instance" << endl; }
17 
18     class AutoRelease
19     {
20     public:
21         AutoRelease() { cout << "AutoRelease Constructor " << endl; }
22 
23         ~AutoRelease()
24         {
25             if (m_instance != nullptr)
26             {
27                 delete(m_instance);
28                 m_instance = nullptr;
29                 cout << "AutoRelease Destructor" << endl;
30             }
31         }
32     };
33 };
34 
35 Singleton* Singleton::m_instance = nullptr;
36 
37 Singleton * Singleton::Instance()
38 {
39     if (m_instance == nullptr)
40     {
41         m_instance = new Singleton;
42         static AutoRelease auto_release;
43     }
44     return m_instance;
45 }
46 
47 int main()
48 {
49     Singleton::Instance()->printInstance();
50     Singleton::Instance()->printInstance();
51 
52     return 0;
53 }

運行結果:

如此就能正常析構了。

如此思路重構了代碼,memeryleak裏面便少了這部分的內存泄漏記錄。

相關文章
相關標籤/搜索