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.
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
} *
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>
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>}
où 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