如何判斷 C++ 物件的類別?

使用 C++ 開發系統時,常遇到某個物件被 release 多次。你也許會想知道該 物件原屬於哪一個 class,特別是有繼承關係時。如果能知道該物件是由哪一個 sub-class 產生的,或許有助於 debugging。

每一個具有 virtual method 的 class 都有一個對應的 vtable,記錄每一個 virtual method 的位址。而該 class 的 instance 則包括一個指向 vtable 的 pointer,且該 pointer (vptr) 通常在 instance 的開頭位址 (以 Linux 為例), 如果一個 object 被多次 release, 運氣好時, vptr 沒被破壞,就可透過 vptr 取得 vtable 的位址,透過 vtable 反推該 instance 是屬於哪個 class。 (雖然不是 100%,但有極高的可信度。)

在大部分(我所遇過)的 Linux 平臺,vptr 都是在 instance 的起始位址。因此, 你能在 gdb 裡直接將該 instance casting 成一個 (void *),取得 vtable 的位址。

(gdb) print *(unsigned **)obj

vtable 可以看成一個 array,每一個 element 都是一個指向 function 的 pointer。因此,你可以讀出每一個 virtual method 的位址。

(gdb) print /x (*(unsigned **)obj)[0]

有了 virtual method 的位址,你還需要將該位址對應至實際的 symbol。

(gdb) info symbol (*(unsigned **)this)[0]
nsGlobalWindow::QueryInterface(nsID const&, void**) + 1 in section .text of
     /home/thinker/progm/B2G/objdir-gecko/dist/bin/libxul.so

透過此指令,你能將所有的 virtual method 都列出來,進而推斷出 class name。

另外,也可以直接查 vtable 的 symbol。

(gdb) info symbol *(unsigned *)this
vtable for nsGlobalWindow + 8 in section .data.rel.ro of
     /home/thinker/progm/B2G/objdir-gecko/dist/bin/libxul.so
(gdb)

雖然這些指令所得到的結果,可能因為 optimize 的原故,不保證百分之百正確。 但大部分情況下還是有用的。若有這樣的需求,不妨試試!