Подтвердить что ты не робот

Как определить, является ли объект экземпляром определенного производного класса С++ из указателя на базовый класс в GDB?

Я отлаживаю программу С++ с помощью GDB.

У меня есть указатель на объект определенного класса. Указатель объявлен как некоторый суперкласс, который расширен несколькими подклассами.

В объекте нет полей для указания точного типа класса этого объекта, но некоторые виртуальные функции (например, bool is_xxx()) определены для указания типа класса во время выполнения.

Есть ли способ рассказать точный тип класса объекта в GDB без вызова этих виртуальных функций. Вызов таких функций в GDB может вызвать запутывающий результат, когда программа многопоточная.

4b9b3361

Ответ 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