Java 有保證在第一次使用 class 內任何東西時, 會執行 class initialization 的程式。而 C++ 沒有保證不同編譯單元的 non-local static 物件的初始順序。
- 編譯單元是指同一 object file 的原始碼和其引入的檔案
- static 物件指不在 heap 也不在 stack 上的東西, 包含全域變數、namespace 上的變數、class 內 static member 等
這會導致一個可怕的事實: 使用另一個 object file 的 namespace 下的物件時 (這是滿常見的需求), 會導致未定義的行為。《Effective C++ 3/e》item 4 的建議解法是用函式存 local static 物件, 然後用函式取得該物件 (類似 singleton 的概念)。
用程式來說, 就是:
// Bad! Lead to an undefined behavior if clients access tfs. FileSystem tfs; // Good! FileSystem& tfs() { static FileSystem fs; return fs; }
附帶一提, 不同語言的規範差真多, 之前看 ARM 的組語, 才發覺之前只知道 x86 的東西, 而對 CPU 有些誤解。現在確信只學單一工具會有風險, 思維會受限於該工具, 誤以為那就是世界。人生有許多事也是如此, 想到莊子所說的話:
井蛙不可以語於海者,拘於虛也;﹔夏蟲不可以語於冰者,篤於時也;曲士不可以語於道者,束於教也。
體悟漸多後, 漸漸也不知要寫什麼心得才好。總之就是多看看, 時時提醒自己所知很窄, 別錯將一種方法當作只有這種方法。
這tfs() 有點像 singleton pattern,我認為這種static只適用於 POD,或是contructor內沒有再depend on其他CRT或其他class。
回覆刪除如果constructor內真的要用CRT的話,為何不用lazy-initialize?
呃... POD 和 CRT 是什麼的縮寫?
回覆刪除至於 lazy initialization, 我個人偏好能不用就不用, 雖然方便, 但不容易掌握其行為。一堆 lazy initialization 串在一起時, 很難在腦中描繪清楚整個藍圖, 明白各元件的生命週期。也可能是我不熟這種作法, 才會有這種感覺吧。之前看 Django 原始碼時, 雖然明白這樣作可省下許多沒用到模組的初始化成本, 但覺得這種寫法不好讀, 而且可能也因方便亂用, 而將相依關係變更亂。