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++ 愈多, 愈覺得要從實作層面才能理解它的語法。
程式碼
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
class B; | |
class A | |
{ | |
public: | |
A() : x(0) {} | |
A(const B& b) { printf("copy constructor A is called\n"); } | |
void set_x(int x) { this->x = x * 10; } | |
int x; | |
}; | |
class A; | |
class B : public A | |
{ | |
public: | |
B() : x(0) {} | |
void set_x(int x) | |
{ | |
this->x = x; | |
printf("%d %d\n", x, A::x); | |
static_cast<A>(*this).set_x(x); | |
printf("%d %d\n", x, A::x); | |
static_cast<A*>(this)->set_x(x); | |
printf("%d %d\n", x, A::x); | |
A::set_x(x); | |
printf("%d %d\n", x, A::x); | |
} | |
int x; | |
}; | |
class C : public A | |
{ | |
public: | |
void set_x(int x) | |
{ | |
this->x = x; | |
printf("%d %d\n", x, A::x); | |
static_cast<A>(*this).set_x(x); | |
printf("%d %d\n", x, A::x); | |
static_cast<A*>(this)->set_x(x); | |
printf("%d %d\n", x, A::x); | |
A::set_x(x); | |
printf("%d %d\n", x, A::x); | |
} | |
// Does not have "C.x". | |
}; | |
int main(void) | |
{ | |
B b; | |
printf("\nb.set_x(3)\n"); | |
b.set_x(3); | |
C c; | |
printf("\nc.set_x(3)\n"); | |
c.set_x(3); | |
printf("\nmethod address\n"); | |
printf("&A::set_x %p\n", &A::set_x); | |
printf("&B::set_x %p\n", &B::set_x); | |
printf("&C::set_x %p\n", &C::set_x); | |
printf("\nmember field address\n"); | |
printf("&b.x %p\n", &b.x); | |
printf("&((A*)&b)->x %p\n", &((A*)&b)->x); | |
printf("&c.x %p\n", &c.x); | |
printf("&((A*)&c)->x %p\n", &((A*)&c)->x); | |
return 0; | |
} |
範例輸出
$ ./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
沒有留言:
張貼留言