Как определить, является ли объект экземпляром определенного производного класса С++ из указателя на базовый класс в GDB?
Я отлаживаю программу С++ с помощью GDB.
У меня есть указатель на объект определенного класса. Указатель объявлен как некоторый суперкласс, который расширен несколькими подклассами.
В объекте нет полей для указания точного типа класса этого объекта, но некоторые виртуальные функции (например, bool is_xxx()) определены для указания типа класса во время выполнения.
Есть ли способ рассказать точный тип класса объекта в GDB без вызова этих виртуальных функций. Вызов таких функций в GDB может вызвать запутывающий результат, когда программа многопоточная.
Ответы
Ответ 1
Используйте ptype
. Если вы используете его самостоятельно, вы получите объявленный тип указателя:
(gdb) ptype ptr
type = class SuperClass {
// various members
} *
Чтобы получить фактический тип указанного объекта, установите переменную "print object":
(gdb) set print object on
(gdb) ptype ptr
type = /* real type = DerivedClass * */
class SuperClass {
// various members
} *
Ответ 2
В моей системе ptype или whatis также показывают только очевидное.
(gdb) whatis pObject
type = QObject *
Но печать первой записи vtable помогла мне:
(gdb) p /a (*(void ***)pObject)[0]
$4 = 0xb4b4cdf4 <QMessageBox::metaObject() const>
Здесь pObject указывает на QMessageBox, который получен из QObject.
Это работает только в том случае, если vtable-entry указывает на метод, который переопределяется производным классом.
См. также:
Печать С++ vtables с использованием GDB
Изменить: печать только указателя на vtable работает более надежно (хотя выход использует искаженное имя и не читается):
(gdb) p /a (*(void ***)pObject)
$5 = 0xb4af33a0 <_ZTV11QMessageBox+8>
Ответ 3
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;
}
Ответ 4
Вам не нужно вызывать виртуальные функции, вы можете просто увидеть адрес виртуальной функции или vtable.
Другой способ - использовать RTTI