271 votes

g++ référence indéfinie à typeinfo

Je viens de tomber sur l'erreur suivante (et j'ai trouvé la solution en ligne, mais elle n'est pas présente dans Stack Overflow) :

(.gnu.linkonce. [stuff]) : non défini référence à [méthode] [objet fichier] :(.gnu.linkonce.[stuff]) : référence indéfinie à `typeinfo for [nom de classe]'.

Pourquoi peut-on obtenir une de ces erreurs de liaison "référence indéfinie à typeinfo" ?

(Points bonus si vous pouvez expliquer ce qui se passe dans les coulisses).

51 votes

Je sais que c'est un vieux post, mais j'ai eu le même problème aujourd'hui, et la solution était simplement de définir ma fonction virtuelle comme virtual abc() {} dans la classe de base, au lieu de virtual abc() ; qui donnait l'erreur.

31 votes

Mieux encore comme virtual void abc() =0; (si la version de base n'est jamais appelée)

3 votes

@Nav : Si vous définissez abc() comme ça, vous pouvez facilement oublier de redéfinir abc() dans la classe dérivée et penser que tout va bien, puisque vous pourrez toujours appeler la fonction sans problème. Une bonne pratique pour l'implémentation de fonctions virtuelles pures se trouve dans cet article Le but est de faire en sorte que la fonction imprime "Pure virtual function called" et fasse planter le programme.

278voto

paxdiablo Points 341644

Une raison possible est que vous déclarez une fonction virtuelle sans la définir.

Lorsque vous le déclarez sans le définir dans la même unité de compilation, vous indiquez qu'il est défini ailleurs - ce qui signifie que la phase de liaison essaiera de le trouver dans l'une des autres unités de compilation (ou bibliothèques).

Voici un exemple de définition de la fonction virtuelle :

virtual void fn() { /* insert code here */ }

Dans ce cas, vous attachez une définition à la déclaration, ce qui signifie que l'éditeur de liens n'a pas besoin de la résoudre ultérieurement.

La ligne

virtual void fn();

déclare fn() sans le définir et provoquera le message d'erreur que vous avez demandé.

C'est très similaire au code :

extern int i;
int *pi = &i;

qui stipule que le nombre entier i est déclarée dans une autre unité de compilation qui doit être résolue au moment de la liaison (autrement pi ne peut pas être réglé sur son adresse).

34 votes

Il est incorrect de dire que virtual void fn() = 0 est une définition. Ce n'est pas une définition, mais une simple déclaration . La seule raison pour laquelle le linker n'essaie pas de la résoudre est que l'entrée VMT correspondante ne fera pas référence à un corps de fonction (elle contiendra très probablement un pointeur nul). Cependant, rien ne vous interdit d'appeler cette fonction virtuelle pure d'une manière non virtuelle, c'est-à-dire en utilisant un nom entièrement qualifié. Dans ce cas, l'éditeur de liens sera cherchez le corps, et vous devrez définir la fonction. Et oui, vous puede définir un corps pour une fonction virtuelle pure.

1 votes

Et parfois, on doit même déclarer un corps pour une fonction virtuelle pure.

3 votes

Le compilateur (g++) vous dira quel est le symbole manquant. Note : En cas de liaison de bibliothèque dynamique, vous pouvez obtenir un nom mangé. Utilisez c++filt <mangledNameVariable> pour l'obtenir sous une forme lisible. L'erreur de typeinfo avec un nom de classe était dans mon cas à cause d'une implémentation de destructeur virtuel manquante dans une certaine classe de base.

184voto

Sergiy Byelozyorov Points 2058

Cela peut également se produire lorsque vous mélangez -fno-rtti y -frtti code. Ensuite, vous devez vous assurer que toute classe, qui type_info est accessible dans le -frtti ont leur méthode clé compilée avec -frtti . Un tel accès peut se produire lorsque vous créez un objet de la classe, que vous utilisez la fonction dynamic_cast etc.

[ fuente ]

26 votes

MERCI BEAUCOUP. Cela a réglé mon problème après 5 heures de recherche.

0 votes

@steipete : content que cela soit utile :)

1 votes

Le lien source est mort, il s'agissait sûrement de la même chose que permalink.gmane.org/gmane.comp.gcc.help/32475

71voto

cdleary Points 18869

Cela se produit lorsque des fonctions virtuelles déclarées (non pures) sont dépourvues de corps. Dans votre définition de classe, quelque chose comme :

virtual void foo();

Doit être défini (en ligne ou dans un fichier source lié) :

virtual void foo() {}

Ou déclarée purement virtuelle :

virtual void foo() = 0;

30voto

CesarB Points 18048

Citation de le manuel de gcc :

Pour les classes polymorphes (classes avec des fonctions virtuelles), l'objet type_info est écrit avec la vtable [...] Pour tous les autres types, nous écrivons l'objet type_info quand il est utilisé : quand on applique `typeid' à une expression, quand on lance un objet, ou quand on fait référence à un type dans une clause catch ou une spécification d'exception.

Et un peu plus tôt sur la même page :

Si la classe déclare des fonctions virtuelles non inline et non pures, la première est choisie comme "méthode clé" pour la classe, et la vtable n'est émise que dans l'unité de traduction où la méthode clé est définie.

Donc, cette erreur se produit lorsque la "méthode clé" n'a pas de définition, comme d'autres réponses l'ont déjà mentionné.

2 votes

Dans mon cas, j'avais une classe de base qui déclarait mais ne définissait pas les méthodes virtuelles qui n'étaient pas purement virtuelles. Une fois que je les ai rendues virtuelles pures, ce qui est ce que je voulais dire, les erreurs de liaison ont disparu.

0 votes

@TatianaRacheva Merci ! Le rapport d'erreur de l'éditeur de liens n'est pas très utile et pour une grande interface, il est très facile de manquer l'absence de '=0;' pour des raisons purement virtuelles !

27voto

human Points 111

Si vous liez un .so à un autre, une autre possibilité est de compiler avec "-fvisibility=hidden" dans gcc ou g++. Si les deux fichiers .so ont été construits avec "-fvisibility=hidden" et que la méthode clé n'est pas dans le même .so qu'une autre implémentation de la fonction virtuelle, cette dernière ne verra pas la vtable ou le typeinfo du premier. Pour l'éditeur de liens, cela ressemble à une fonction virtuelle non implémentée (comme dans les réponses de paxdiablo et cdleary).

Dans ce cas, vous devez faire une exception pour la visibilité de la classe de base avec

__attribute__ ((visibility("default")))

dans la déclaration de la classe. Par exemple,

class __attribute__ ((visibility("default"))) boom{
    virtual void stick();
}

Une autre solution, bien sûr, est de ne pas utiliser "-fvisibility=hidden". Cela complique les choses pour le compilateur et l'éditeur de liens, peut-être au détriment des performances du code.

2 votes

Il n'est pas nécessaire d'exporter (dé-cacher) la classe de base si elle est abstraite ou inutilisée, seulement les fonctions non virtuelles, normalement juste le constructeur. Le site dérivé de En revanche, les classes doivent être exportées si elles sont utilisées.

0 votes

Ça ressemble à un hack, mais ça a résolu les symptômes de mon côté. Merci !

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X