Как определить, является ли объект экземпляром определенного производного класса C ++ из указателя на базовый класс в GDB? - PullRequest
44 голосов
/ 16 декабря 2011

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

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

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

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

Ответы [ 4 ]

51 голосов
/ 16 декабря 2011

Используйте 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
} *
16 голосов
/ 16 декабря 2014

В моей системе ptype или whatis также показывают только очевидное.

(gdb) whatis pObject
type = QObject *

Но печать первой записи vtable помогла мне:

(gdb) p /a (*(void ***)pObject)[0]
$4 = 0xb4b4cdf4 <QMessageBox::metaObject() const>

Здесь pObject указывает на QMessageBox, который является производным от QObject. Это работает, только если vtable-entry указывает на метод, который переопределяется производным классом.

Смотрите также: Печать таблиц C ++ с использованием GDB

Редактировать: печать только указателя на vtable работает более надежно (хотя вывод использует искаженное имя и не так читается):

(gdb) p /a (*(void ***)pObject)
$5 = 0xb4af33a0 <_ZTV11QMessageBox+8>
7 голосов

ГБД 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;
}
2 голосов
/ 16 декабря 2011

Вам не нужно вызывать виртуальные функции, вы можете просто увидеть адрес виртуальной функции или vtable.Другой способ - использовать RTTI

.
...