Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
認識 C++ constructor,從Singleton談起             Luba Tang             2010/5/11
什麼是 singleton?• 在系統中 唯一 存在 的物件 – 唯一性 – 存在性 例子 1. 集中式資料庫 embeded dbm 2. 棋局中的棋盤 3. Register File
傳統做法 – 全域變數以棋盤為例子• 在棋盤的 header file 中宣告為全域變數 Chess Board[20][20];• 在 implementation file 中以外部變數使用 extern Chess Board[20][2...
/***************************************** * board.h                               * *************************************...
用全域變數做 singleton 產生的問題• 在多人同時開發之下,存在性與唯一性不  太容易保留/***************************************** * user.h                      ...
用全域變數做 singleton 產生的問題• 你真的確定你的型別一致嗎?// lib1.c                             // lib2.cunsigned int a;                       ...
Linker 解析 symbol 的順序• Linker 用下列兩個 rules 解析 symbol   – 不能存在兩個強符號   – 強符號 > 弱符號   – 大空間 > 小空間• 在 gcc C compiler 當中   – 有初始值...
Global variable 的缺點1. 不保證存在性 不保證唯一性 – 允許 copy, build, delete2. 難移維護 – 使用到棋盤的物件都必須要知道棋盤的類別,   一旦棋盤類別被修改,就必須全部重新修改
C++ 作法                                      提供唯一的進入點class Board                     如果 instance 沒有 new 過,就 new 一個新{       ...
Compiler 看 singletonC++ static member variable  – 無論有沒有初始值,都視為 有初始值的    global variable,為強符號  – 強符號具有 linking view 上的唯一性 ...
問題• 優點 – 不必再知道棋盤的類別,Board::self() 就可   以取得棋盤• 缺點 – 我們只保證了 linking view,尚未保證 run-   time behavior – 存在性尚未保證 delete Board::s...
C++ 作法 (2)class Board{public:                                  將必要函式加入 private      static Board* self(); Compiler 會自動幫類別加...
問題• 存在性確保了嗎? – YES,無法任意 delete、new 物件• 唯一性確保了嗎? – No,允許 copy assignment 和 copy   constructor – Board b( *Board::self() ); ...
C++ 作法 (3)class Board{public:                            將必要函式加入 private                             Compiler 會自動幫類別加入這四個 ...
背景知識 – static local variableint Fun(){  static int x = 100;                                使用 static  return ++x;         ...
Meyers’ Singleton                                  提供唯一的進入點class Board                static local variable 表示只有在執行第一次的{  ...
See Again – Meyers’ Singletonclass Board{public:        static Board* self() {                static Board Instance;      ...
Compiler 來看 Static local variable normal local variable 稱做 auto-variable,會放在  stack or register 當中  – 通常會被 compiler optim...
從 compiler 觀點看 Meyer’s Singletonclass Board                                            何時執行該{                             ...
Variable = storage type + Duration C++ Storage   – Static storage       • 用 static 宣告出來的物件,包含 member variable   – Automat...
Static storage initialization 的時機 在 standard 中如是說:     Static storage 在所有其他 initialization 開始之前,就要先 zero initialization ...
See Again – Meyers’ Singletonclass Board{public:        static Board* self() {                static Board Instance();    ...
新問題 – Dead Reference Problem• 在系統結束時,設計不良的 singleton 很  有可能被消滅很多次,造成 segmentation  fault
Dead Reference Problem• 例子 – 三個 singleton 物件 – Board, Game, Log – 任何 singleton 發生問題,就會結束整個系統 – 所有物件的 destructor   • Log::s...
C++ 的static storage結束方式 - FILO•   Static storage 的 duration是main之前到 main 之後•   Static storage 是先呼叫的後結束,在建立物件的時候    就決定了物件消...
解法一 偵測出 Dead Reference• 多加一個 static bool m_bDestroy 紀錄是  否被消滅過• 在 Log constructor 中加判斷式,判斷是  否被摧毀過
On Dead Singleton (1/2)class Log                                                判斷為何 pointer 為 0{                         ...
On Dead Singletonprivate:                                   Meyers’ Singleton       static void create() {           標準 co...
On Dead Singleton (1/2)class Log                                  判斷為何 pointer 為 0{                                 onDead...
問題• 優點 – 不會發生 segmentation fault,由錯誤處理物   件負責處理 exception• 缺點 – 需要額外的錯誤處理物件,而錯誤處理物件   往往就是問題發生的元兇之一 (如本例的 Log   物件) – 沒有真正...
Phoenix Singleton•    解決方法    – 希望在 onDeadReference() 中能夠重新初      始化 Log,讓死掉的 Log 復活,繼續工作•    關於復活重要的四件事    1.   同樣的記憶體位置 ...
背景資料• Placement Operator new()  – 語法    new(address) class_name();  – 意義    1. 以 address 為起點,建立一個 class_name 的物件    2. 不會去...
Phoenix Singleton                                    取得已死的指標private:                       create 雖然無法再取得新       static vo...
完整的 Phoenix Singleton (1/3)class Log{public:        static Log* self() {                if( m_pInstance == 0 ) {          ...
完整的 Phoenix Singleton (2/3)private:       static void onDeadReference() {               create();               new(m_pIns...
完整的 Phoenix Singleton (3/3)private:        ~Log() {                m_pInstance = 0;                m_bDestory = true;     ...
可能不是不死鳥,只是 32+ 命怪貓atexit 只需要支援 “註冊 32 個 functions”GCC 好家在 Libiberty 提供 xatexit,沒有限制
Singleton 優劣分析 優點 – 好用、好實作 – 比 global variable 多很多安全性 缺點 – 太好用,常會讓人忘記其毀滅關係上的困難• 不可以偷懶! 要先規劃好生成與毀滅關係,最  後才能決定要不要使用 single...
進階主題• Thread-safe singleton  – Is Meyer’s singleton thread-safed?  – If not, please implement one• Template singleton  – R...
從 Singleton 談 constructor
Upcoming SlideShare
Loading in …5
×

從 Singleton 談 constructor

2,911 views

Published on

2012/10/25 新竹碼農

  • Be the first to comment

從 Singleton 談 constructor

  1. 1. 認識 C++ constructor,從Singleton談起 Luba Tang 2010/5/11
  2. 2. 什麼是 singleton?• 在系統中 唯一 存在 的物件 – 唯一性 – 存在性 例子 1. 集中式資料庫 embeded dbm 2. 棋局中的棋盤 3. Register File
  3. 3. 傳統做法 – 全域變數以棋盤為例子• 在棋盤的 header file 中宣告為全域變數 Chess Board[20][20];• 在 implementation file 中以外部變數使用 extern Chess Board[20][20];
  4. 4. /***************************************** * board.h * *****************************************/class BoardType{};static BoardType Board;/***************************************** * user.h * *****************************************/#include “board.h”extern BoardType Board;
  5. 5. 用全域變數做 singleton 產生的問題• 在多人同時開發之下,存在性與唯一性不 太容易保留/***************************************** * user.h * *****************************************/#include “board.h”extern BoardType Board;BoardType MyBoard = Board;MyBoard->setChess( black, 20 );
  6. 6. 用全域變數做 singleton 產生的問題• 你真的確定你的型別一致嗎?// lib1.c // lib2.cunsigned int a; char a;// main.cextern a; 他到底是誰?[user@locahost]$ gcc –c ./lib1.c && gcc –c ./lib2.c && gcc –c ./main.c[user@locahost]$ gcc ./lib1.o ./lib2.o ./main.o
  7. 7. Linker 解析 symbol 的順序• Linker 用下列兩個 rules 解析 symbol – 不能存在兩個強符號 – 強符號 > 弱符號 – 大空間 > 小空間• 在 gcc C compiler 當中 – 有初始值的 global variable 為強符號 – 沒有初始值的 global variable 為弱符號,屬於 COMMON variable處理// lib1.c // lib2.cunsigned int a; char a;// main.cextern a; 是 unsigned int
  8. 8. Global variable 的缺點1. 不保證存在性 不保證唯一性 – 允許 copy, build, delete2. 難移維護 – 使用到棋盤的物件都必須要知道棋盤的類別, 一旦棋盤類別被修改,就必須全部重新修改
  9. 9. C++ 作法 提供唯一的進入點class Board 如果 instance 沒有 new 過,就 new 一個新{ 的物件﹔否則就把之前 new 過的物件傳回去public: static Board* self() { if( m_pInstance == 0 ) m_pInstance = new Board(); return m_pInstance; 真正的物件 } 必須要宣告為 static,以保證不會private: 隨著特定物件而消失 Board(); static Board *m_pInstance; 前置宣告}; 需要在實作檔中,前置宣告 instance,來初始化指標空間Board *Board::m_pInstance = 0;
  10. 10. Compiler 看 singletonC++ static member variable – 無論有沒有初始值,都視為 有初始值的 global variable,為強符號 – 強符號具有 linking view 上的唯一性 – 強符號具有 linking view 上的存在性
  11. 11. 問題• 優點 – 不必再知道棋盤的類別,Board::self() 就可 以取得棋盤• 缺點 – 我們只保證了 linking view,尚未保證 run- time behavior – 存在性尚未保證 delete Board::self(); // 合法
  12. 12. C++ 作法 (2)class Board{public: 將必要函式加入 private static Board* self(); Compiler 會自動幫類別加入這兩個private: member function 到 public section中 強制加入到 private section Board(); ~Board();private: static Singleton *m_pInstance;}
  13. 13. 問題• 存在性確保了嗎? – YES,無法任意 delete、new 物件• 唯一性確保了嗎? – No,允許 copy assignment 和 copy constructor – Board b( *Board::self() ); // 合法,但不是我們想要的
  14. 14. C++ 作法 (3)class Board{public: 將必要函式加入 private Compiler 會自動幫類別加入這四個 static Board* self(); member function 到 public section中 強制加入到 private sectionprivate: Board(); ~Board(); Board( const Board& ); Board& operator=( const Board& );private: static Singleton *m_pInstance;}
  15. 15. 背景知識 – static local variableint Fun(){ static int x = 100; 使用 static return ++x; 語意: 生成 function level 的全域變數} 不同的執行期間,會看到同樣的記憶體 空間,同樣的變數
  16. 16. Meyers’ Singleton 提供唯一的進入點class Board static local variable 表示只有在執行第一次的{ 時候會被初始化,相當於之前 if-else 語法public: static Board* self() { static Board Instance; return &Instance; } 真正的物件private: 利用 static function 的特性,只有在函式被呼 Board(); 叫的時候才會有變數被生成 ~Board(); Board( const Board& ); 前置宣告 Board& operator=( const Board& ); 移除前置宣告,程式碼乾乾淨}; 淨
  17. 17. See Again – Meyers’ Singletonclass Board{public: static Board* self() { static Board Instance; return &Instance; }private: Board(); ~Board(); Board( const Board& ); Board& operator=( const Board& );};
  18. 18. Compiler 來看 Static local variable normal local variable 稱做 auto-variable,會放在 stack or register 當中 – 通常會被 compiler optimization 消除 – Debugger 和 exception handling 需要有額外的 compensation code 來還原被削除的 auto-variable Static local variable 被放在 data/bss section 當中 – 大多數狀況下不會被 compiler optimization 消除 – gcc 不會消除 – 記憶體空間固定
  19. 19. 從 compiler 觀點看 Meyer’s Singletonclass Board 何時執行該{ constructor?public: static Board* self() { static Board Instance; return &Instance; 阿就第一次執行的時候啊! } constructor 不過是另一個private: function Board(); ~Board(); Board( const Board& ); Board& operator=( const Board& );};
  20. 20. Variable = storage type + Duration C++ Storage – Static storage • 用 static 宣告出來的物件,包含 member variable – Automatic storage • Auto, register 或者 NOT static, extern 宣告出來的物件 – Dynamic storage • 用 new 做出來的物件 Storage Duration – Automatic duration • 用 register 或 auto 宣告出來的變數,相依於 scope – Dynamic duration • 從 new 開始,delete 結束 – Static duration • 從程式開始到結束
  21. 21. Static storage initialization 的時機 在 standard 中如是說:  Static storage 在所有其他 initialization 開始之前,就要先 zero initialization  Non-local variable,不論任何 storage type,Static initialization 要在 dynamic initialization 之前  Zero initialization  Initialization with constant expression Local static variable 的 initialization 比較複雜 – 允許 compiler 有自己的 implementation 1. 可以在 namespace scope 開始時 initialization 2. 可以在 first time control passes through the declaration 開始時幫忙 initialize GCC Compiler 當中這樣做: – Static storage 有值擺 .data – Static storage 沒值擺 .bss – Local static POD (C data type) 走 1 – 其他 local static variable 走 2
  22. 22. See Again – Meyers’ Singletonclass Board{public: static Board* self() { static Board Instance(); ; 目前 gcc 不給過 return &Instance; }private: Board(); ~Board(); Board( const Board& ); Board& operator=( const Board& );};
  23. 23. 新問題 – Dead Reference Problem• 在系統結束時,設計不良的 singleton 很 有可能被消滅很多次,造成 segmentation fault
  24. 24. Dead Reference Problem• 例子 – 三個 singleton 物件 – Board, Game, Log – 任何 singleton 發生問題,就會結束整個系統 – 所有物件的 destructor • Log::self()->printf( “Dead Mesg” ); • Clean up member variables
  25. 25. C++ 的static storage結束方式 - FILO• Static storage 的 duration是main之前到 main 之後• Static storage 是先呼叫的後結束,在建立物件的時候 就決定了物件消滅的時間 • Game出問題 • Call Log::self() 1. Log::Log() 2. Log::printf() Game::Game() • Throw exception • Log::~Log(); Board::~Board() • Board::~Board() • Log::self()->printf(); Segmentation fault
  26. 26. 解法一 偵測出 Dead Reference• 多加一個 static bool m_bDestroy 紀錄是 否被消滅過• 在 Log constructor 中加判斷式,判斷是 否被摧毀過
  27. 27. On Dead Singleton (1/2)class Log 判斷為何 pointer 為 0{ onDeadReference() 會傳回 errorpublic: create() 會建立新的 Log 物件 static Log* self() { if( m_pInstance == 0 ) { if( m_bDestroy ) onDeadReference(); else create(); } return m_pInstance; }private: static bool m_bDestory; Log* m_pInstance;
  28. 28. On Dead Singletonprivate: Meyers’ Singleton static void create() { 標準 constructor 寫法 static Log instance; m_pInstance = &instance; } static void onDeadReference() { throw std::runtime_error(”Dead Occurs”); } 避免 segmentation fault ~Singleton() { 傳回一個 exception,把問題 m_pInstance = 0; 丟出去 m_bDestory = true; }}; 不使用 delete 只需要把 pointer 設為 0,不可以 delete m_pInstance,系統會自動 destroy instance
  29. 29. On Dead Singleton (1/2)class Log 判斷為何 pointer 為 0{ onDeadReference() 會傳回 errorpublic: create() 會建立新的 Log 物件 static Log* self() { if( m_pInstance == 0 ) { if( m_bDestroy ) onDeadReference(); else create(); } return m_pInstance; } Private instance pointer改為private: static static bool m_bDestory; static Log* m_pInstance; Log* m_pInstance;
  30. 30. 問題• 優點 – 不會發生 segmentation fault,由錯誤處理物 件負責處理 exception• 缺點 – 需要額外的錯誤處理物件,而錯誤處理物件 往往就是問題發生的元兇之一 (如本例的 Log 物件) – 沒有真正的解決問題,只做到認知問題而已
  31. 31. Phoenix Singleton• 解決方法 – 希望在 onDeadReference() 中能夠重新初 始化 Log,讓死掉的 Log 復活,繼續工作• 關於復活重要的四件事 1. 同樣的記憶體位置 2. 同樣的記憶體大小 3. 不同於死前的記憶體內容 4. 必須指定何時再去死一次
  32. 32. 背景資料• Placement Operator new() – 語法 new(address) class_name(); – 意義 1. 以 address 為起點,建立一個 class_name 的物件 2. 不會去註冊 atexit• atexit – 語法 int atexit( void (*pFun)() ); – 意義 註冊結束時應該執行哪一個 function, FILO stack
  33. 33. Phoenix Singleton 取得已死的指標private: create 雖然無法再取得新 static void 物件,但是可以取得死亡 onDeadReference() { 物件的指標 create(); new(m_pInstance) Log; 在相同位置建立新物件 atexit(killLog); 物件狀態會回覆到初始 化狀態,無記憶功能 m_bDestory = false; } 註冊死亡函式 static void killLog() { 其實是間接呼叫 destructor, m_pInstance->~Log(); 凡是有 operator new 就一定 要有 atexit }}; 死亡函式 間接呼叫destructor
  34. 34. 完整的 Phoenix Singleton (1/3)class Log{public: static Log* self() { if( m_pInstance == 0 ) { if( m_bDestroy ) onDeadReference(); else create(); } return m_pInstance; }
  35. 35. 完整的 Phoenix Singleton (2/3)private: static void onDeadReference() { create(); new(m_pInstance) Log; atexit(killLog); m_bDestory = false; } static void killLog() { m_pInstance->~Log(); } static void create() { static Log instance; m_pInstance = &instance; }
  36. 36. 完整的 Phoenix Singleton (3/3)private: ~Log() { m_pInstance = 0; m_bDestory = true; } // constructor, copy constructor, and assignment …private: static bool m_bDestory; static Log* m_pInstance;};// in cppbool Log::m_bDestory = false;Log* Log::m_pInstance = 0;
  37. 37. 可能不是不死鳥,只是 32+ 命怪貓atexit 只需要支援 “註冊 32 個 functions”GCC 好家在 Libiberty 提供 xatexit,沒有限制
  38. 38. Singleton 優劣分析 優點 – 好用、好實作 – 比 global variable 多很多安全性 缺點 – 太好用,常會讓人忘記其毀滅關係上的困難• 不可以偷懶! 要先規劃好生成與毀滅關係,最 後才能決定要不要使用 singleton。• 通常規畫好之後,singleton數量會很少
  39. 39. 進階主題• Thread-safe singleton – Is Meyer’s singleton thread-safed? – If not, please implement one• Template singleton – Refer to template<typename T> llvm::ManagedStatic• Longevity singleton

×