原本打算快速掃過一遍《Effective C++ 3/e》, 有個大略印象, 之後再視需求仔細看相關守則, 但看到一半發現忘了一堆前面看過的東西。還是來寫寫筆記好了。
- item 07: declare destructors virtual in polymorphic base classes。這樣 delete base pointer 時, 才會呼叫到真正的 destructor, 然後正確地依序往上呼叫各類別的 destructor。
- item 09: never call virtual functions during construction or destruction。理由是 C++ 在 constructor / destructor 裡沒有多型的效果, 因為 C++ 認為在建構或解構時呼叫子類別的函式有危險, 有可能用到未定義的資料, 乾脆定成在 constructor / destructor 裡沒有多型的效果
- 續上則, 補充說明原因。建構式的呼叫順序是由上往下, 而解構式是由下往上。所以若在建構式呼叫子類別的函式, 該函式可能用到子類別特有的欄位, 這時自然還沒初始化, 導致未定義行為。反之, 若在解構式呼叫子類別的函式, 子類別的解構式已清掉它的欄位, 這時再使用到也會導致未定義行為。
- 續上則, 值得注意的是, 在 Java 裡沒有禁止這麼做。Java 類別欄位的 (class field) 的所有型別都有預設的初始值 (像是 0、false、null 等)。Java 任何時候呼叫任何函式, 都會有多型效果。在建構式或解構式時這麼做, 會呼叫到子類別的函式, 至於這類的行為是否符合需求, 就自行判斷啦。下面附上 Java 的測試碼, 順便測一下欄位和方法的行為, 注意兩者行為不同, 方法有多型效果。
class A { String m = "A.m"; void p() { System.out.println("A.p(): " + m); } A() { System.out.println("A()"); p(); } } class B extends A { String m = "B.m"; B() { System.out.println("B()"); p(); } void p() { System.out.println("B.p(): " + m); } } public class Test { public static void main(String[] args) { A a = new B(); System.out.println("--- After new ---"); a.p(); System.out.println(a.m); System.out.println(((B)a).m); } } /* 執行結果: A() B.p(): null B() B.p(): B.m --- After new --- B.p(): B.m A.m B.m */
沒有留言:
張貼留言