web-dev-qa-db-fra.com

Comment déterminer si un objet est une instance de certaines classes C ++ dérivées d'un pointeur vers une classe de base dans GDB?

Je débogue un programme C++ avec GDB.

J'ai un pointeur sur un objet d'une certaine classe. Le pointeur est déclaré comme appartenant à une super-classe qui est étendue par plusieurs sous-classes.

Il n'y a pas de champs dans l'objet pour spécifier le type de classe précis de cet objet mais certaines fonctions virtuelles (par exemple bool is_xxx ()) sont définies pour indiquer le type de classe lors de l'exécution.

Existe-t-il un moyen de dire le type de classe précis d'un objet dans GDB sans appeler ces fonctions virtuelles. L'appel de telles fonctions dans GDB peut générer un résultat déroutant lorsque le programme est multithread.

45
user1101096

Utilisez ptype. Si vous l'utilisez seul, vous obtenez le type déclaré du pointeur:

(gdb) ptype ptr
type = class SuperClass {
  // various members
} *

Pour obtenir le type réel de l'objet pointé, définissez la variable "print object":

(gdb) set print object on
(gdb) ptype ptr
type = /* real type = DerivedClass * */
class SuperClass {
  // various members
} *
53
Beta

Sur mon système, ptype ou whatis ne montre que l'évidence.

(gdb) whatis pObject
type = QObject *

Mais l'impression de la première entrée de la table virtuelle m'a aidé:

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

Ici, le pObject pointait vers un QMessageBox qui est dérivé de QObject. Cela ne fonctionne que si vtable-entry pointe vers une méthode qui est remplacée par la classe dérivée.

Voir aussi: Imprimer les vtables C++ en utilisant GDB

Edit: L'impression uniquement du pointeur sur la table virtuelle est plus fiable (bien que la sortie utilise le nom modifié et ne soit pas aussi lisible):

(gdb) p /a (*(void ***)pObject)
$5 = 0xb4af33a0 <_ZTV11QMessageBox+8>
16
Joachim

GDB 7.11

Depuis GDB 7.11, GCC 5.3.1, Ubuntu 16.04, faisant juste:

p *myBase

sur quelque chose compilé avec:

gcc -O0 -ggdb3

peut suffire comme il le montre déjà:

$1 = {_vptr.MyBase = 0x400c00 <vtable for MyDerived1+16>}

MyDerived1 est la classe dérivée actuelle que nous recherchons.

Mais si vous le faites en plus:

set print object on

la sortie est encore plus claire et ressemble à:

$1 = (MyDerived1) {<MyBase> = {_vptr.MyBase = 0x400c00 <vtable for MyDerived1+16>}, <No data fields>}

Cela affecte également d'autres commandes comme:

ptype myBase

qui montre:

type = /* real type = MyDerived1 * */
class MyBase {
  public:
    virtual int myMethod(void);
} *

au lieu de:

type = class MyBase {
  public:
    virtual int myMethod(void);
} *

Dans ce cas, il n'y avait aucune indication du type dérivé sans set print object on.

whatis est également affecté:

(gdb) whatis myBase
type = MyBase *
(gdb) set print object on
(gdb) whatis myBase
type = /* real type = MyDerived1 * */
MyBase *

Programme de test:

#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;
}

Vous n'avez pas besoin d'appeler les fonctions virtuelles, vous pouvez simplement voir l'adresse de la fonction virtuelle ou de la table virtuelle. Une autre façon consiste à utiliser RTTI

2
user685684