2013年8月22日 星期四

有繼承的情況下, C++ method 存取到誰的 member field?

Effective C++ item 27 "盡量少做轉型" 提到下列轉型看起來像對的, 實際是錯的:

class SpecialWindow : public Window {
public:
  virtual void onReSize() {
    static_cast<Window>(*this).onResize();
    ...
  }
}

對這點感到很納悶, 於是寫個小程式實驗看看。結果發覺自己沒看清楚, 書上舉例是轉型 Window, 不是轉型 Window*, 所以 Window::onResize() 作用到新產生的物件身上, 結果不同於呼叫 Window::onResize()。

既然已經寫了小程式做實驗, 順便記在這裡供日後備忘。

要點如下:

  • 類別 C 的 method 會存取自己的 member field x, 若 C 本身沒有這個 member field, 會往父類別 A 找。
  • 承上, A 和 C 的 method 存取到的 x 是同一個 x, 也就是 A::x。
  • 若類別 B 宣告和父類別 A 同名稱的 member field x, 則類別 B 和父類別 A 各自擁有一份不同位置的 member field x。
  • 承上, A 的 method 會存取 A::x; B 的 method 存取 B::x。呼叫到誰的 method, 就知道改到誰的 x。

題外話, 允許子類別覆寫或重覆宣告同名 method 或 member field 是個糟糕的主意, 再加上可以轉型, 而且轉型後結果還會不一樣, 真是火上加油啊.......。了解 C++ 愈多, 愈覺得要從實作層面才能理解它的語法。

程式碼

範例輸出

$ ./a

b.set_x(3)
3 0
copy constructor A is called
3 0
3 30
3 30

c.set_x(3)
3 3
3 3
3 30
3 30

method address
&A::set_x     0x4006c8
&B::set_x     0x400710
&C::set_x     0x4007da

member field address
&b.x          0x7fffb4ef43a4
&((A*)&b)->x  0x7fffb4ef43a0
&c.x          0x7fffb4ef43b0
&((A*)&c)->x  0x7fffb4ef43b0

沒有留言:

張貼留言

在 Fedora 下裝 id-utils

Fedora 似乎因為執行檔撞名,而沒有提供 id-utils 的套件 ,但這是使用 gj 的必要套件,只好自己編。從官網抓好 tarball ,解開來編譯 (./configure && make)就是了。 但編譯後會遇到錯誤: ./stdio.h:10...