原本打算快速掃過一遍《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
*/
沒有留言:
張貼留言