GDBでC++プログラムをデバッグしています。
特定のクラスのオブジェクトへのポインタがあります。ポインタは、いくつかのサブクラスによって拡張されたスーパークラスのものであると宣言されています。
オブジェクトには、このオブジェクトの正確なクラスタイプを指定するフィールドはありませんが、実行時にクラスタイプを通知するために、いくつかの仮想関数(bool is_xxx()など)が定義されています。
これらの仮想関数を呼び出さずにGDB内のオブジェクトの正確なクラスタイプを通知する方法はありますか?プログラムがマルチスレッド化されている場合、GDBでこのような関数を呼び出すと、混乱を招く結果が生成される可能性があります。
ptype
を使用します。単独で使用すると、ポインタの宣言された型を取得します。
(gdb) ptype ptr
type = class SuperClass {
// various members
} *
指されているオブジェクトの実際のタイプを取得するには、「印刷オブジェクト」変数を設定します。
(gdb) set print object on
(gdb) ptype ptr
type = /* real type = DerivedClass * */
class SuperClass {
// various members
} *
私のシステムでは、ptypeまたはwhatisも明らかなだけを示しています。
(gdb) whatis pObject
type = QObject *
しかし、vtableの最初のエントリを印刷すると役に立ちました。
(gdb) p /a (*(void ***)pObject)[0]
$4 = 0xb4b4cdf4 <QMessageBox::metaObject() const>
ここで、pObjectはQObjectから派生したQMessageBoxを指しています。これは、vtable-entryが派生クラスによってオーバーライドされたメソッドを指している場合にのみ機能します。
編集:vtableへのポインターのみを出力すると、より信頼性が高くなります(ただし、出力はマングルされた名前を使用しているため、それほど読みやすくありません)。
(gdb) p /a (*(void ***)pObject)
$5 = 0xb4af33a0 <_ZTV11QMessageBox+8>
GDB 7.11
GDB 7.11、GCC 5.3.1、Ubuntu 16.04の時点で、次のことだけを行います。
p *myBase
でコンパイルされたもの:
gcc -O0 -ggdb3
それはすでに示しているように十分かもしれません:
$1 = {_vptr.MyBase = 0x400c00 <vtable for MyDerived1+16>}
ここで、MyDerived1
は現在探している派生クラスです。
しかし、あなたがさらに行う場合:
set print object on
出力はさらに明確になり、次のようになります。
$1 = (MyDerived1) {<MyBase> = {_vptr.MyBase = 0x400c00 <vtable for MyDerived1+16>}, <No data fields>}
これは、次のような他のコマンドにも影響します。
ptype myBase
示す:
type = /* real type = MyDerived1 * */
class MyBase {
public:
virtual int myMethod(void);
} *
の代わりに:
type = class MyBase {
public:
virtual int myMethod(void);
} *
この場合、set print object on
がなければ派生型を示すものはありませんでした。
whatis
も同様に影響を受けます:
(gdb) whatis myBase
type = MyBase *
(gdb) set print object on
(gdb) whatis myBase
type = /* real type = MyDerived1 * */
MyBase *
テストプログラム:
#include <iostream>
class MyBase {
public:
virtual int myMethod() = 0;
};
class MyDerived1 : public MyBase {
public:
virtual int myMethod() { return 1; }
};
class MyDerived2 : public MyBase {
public:
virtual int myMethod() { return 2; }
};
int main() {
MyBase *myBase;
MyDerived1 myDerived1;
MyDerived2 myDerived2;
myBase = &myDerived1;
std::cout << myBase->myMethod() << std::endl;
myBase = &myDerived2;
std::cout << myBase->myMethod() << std::endl;
}
仮想関数を呼び出す必要はありません。仮想関数またはvtableのアドレスを確認できます。別の方法は、RTTIを使用することです