Question

Je reçois des erreurs de liaison du type suivant:

  

Festival.obj: erreur LNK2019:   Symbole externe non résolu & "; public:   void __thiscall Tree :: add (class Price & amp;) "   (? add @? $ Tree @ VPrice @@@@ QAEXAAVPrice @@@ Z)   référencé dans la fonction   __catch $? AddBand @ Festival @@ QAE? AW4StatusType @@ HHH @ Z $ 0

J'avais l'habitude de penser que cela avait à voir avec le mécanisme try-catch, mais depuis, on m'a dit le contraire. Ceci est une version mise à jour de la question.

J'utilise Visual Studio 2008, mais j'ai des problèmes similaires en g ++.

Le code correspondant:

Dans Festival.cpp

#include "Tree.h"
#include <exception>

using namespace std;

class Band{
public:
    Band(int bandID, int price, int votes=0): bandID(bandID), price(price), votes(votes){};
...
private:
...

};

class Festival{
public: 
    Festival(int budget): budget(budget), minPrice(0), maxNeededBudget(0), priceOffset(0), bandCounter(0){};
    ~Festival();
    StatusType AddBand(int bandID, int price, int votes=0);
    ...

private: 
    Tree<Band> bandTree;
    ...

};

StatusType Festival::AddBand(int bandID, int price, int votes){
    if ((price<0)||(bandID<0)){
        return INVALID_INPUT;
    }
    Band* newBand=NULL;
    try{
        newBand=new Band(bandID,price-priceOffset,votes);
    }
    catch(bad_alloc&){return ALLOCATION_ERROR;}
    if (bandTree.find(*newBand)!=NULL){
        delete newBand;
        return FAILURE;
    }
bandTree.add(*newBand);
....
}

Dans Tree.h:

template<class T>
class Tree{
public:
    Tree(T* initialData=NULL, Tree<T>* initialFather=NULL);
    void add(T& newData);
....
private:
....
};

Il est intéressant de noter que je n’ai pas d’erreurs de liaison lorsque j’essaie d’utiliser des fonctions d’arbre lorsque le type T est un type primitif comme un int.

Était-ce utile?

La solution

Y a-t-il Tree.cpp? Si c'est le cas, vous avez peut-être oublié de le lier? Où est l'implémentation de Tree :: add? De plus, je ne vois pas où vous appelez Tree :: add. Je suppose que cela devrait être dans la déclaration try, juste après la nouvelle?

Juste un rappel:

Pour la plupart des compilateurs (c'est-à-dire ceux qui pratiquent la compilation séparée), l'implémentation des fonctions membres d'une classe de modèle doit être visible pendant la compilation du fichier source qui utilise la classe de modèle. Généralement, les gens respectent cette règle en plaçant la mise en oeuvre des fonctions membres dans le fichier d’en-tête.

Peut-être que Tree :: add n'est pas dans l'en-tête? Alors une solution possible dans le cas discuté sera de mettre Tree :: add implementation dans le fichier d’en-tête.

La différence entre les classes normales et les classes de modèle existe parce que les classes de modèle ne sont pas " real " classes - c’est, eh bien, un modèle. Si vous aviez défini votre classe Tree comme une classe normale, le compilateur aurait pu utiliser votre code immédiatement. Dans le cas d’un modèle, le compilateur commence par & Écrire; & Quot; pour vous la vraie classe, en substituant les paramètres du modèle avec les types que vous avez fournis. Maintenant, le compilateur compile les fichiers cpp un par un. Il n'est pas au courant d'autres fichiers cpp et ne peut rien utiliser d'autres fichiers cpp. Disons que votre implémentation de Tree: add ressemble à ceci:

void Tree::add(T& newData)
{
    newData.destroyEverything();
}

C’est tout à fait légitime tant que votre méthode a tout détruit. Lorsque le compilateur compile Class.cpp, il veut s'assurer de ne rien faire avec ce qu'il ne sait pas. Par exemple, Tree<int> ne fonctionnera pas parce que int n'a pas de destruction. Le compilateur essaiera d'écrire votre code avec int au lieu de T et découvrira que le code ne se compile pas. Mais depuis que le compilateur &, "Voit &"; seulement le cpp actuel et tout ce qu'il contient, il ne pourra pas valider la fonction d'ajout, car il se trouve dans un cpp séparé.

Il n'y aura pas de problème avec

void Tree::add(int& newData)
{
    newData.destroyEverything();
}

est implémenté dans un cpp séparé car le compilateur sait qu'int est le seul type acceptable et peut & "compter sur lui-même &"; que, lorsqu'il compilera Tree.cpp, il trouvera l'erreur.

Autres conseils

Êtes-vous sûr que le try / catch a quelque chose à voir avec cela? Que se passe-t-il si vous commentez simplement les lignes Tree::add(class Price &) et <=>, laissez le reste du code tel quel et construisez-le?

Il se peut simplement qu'il vous manque la bibliothèque qui définit <=> à partir de votre ligne de liaison.

Mise à jour: l'utilisation de fonctions d'arborescence avec un type primitif n'entraîne pas d'erreur de liaison. J'ai mis à jour ma question à la lumière de certaines des choses qui ont été dites.

Comme d'autres l'ont déjà indiqué, vous devez afficher l'implémentation de Treee :: add () et nous indiquer comment vous le liez.

Sur un point indépendant, si vous utilisez des constructions telles que:

  Band* newBand=NULL;
    try{
        newBand=new Band(bandID,price-priceOffset,votes);
    }
    catch(bad_alloc&){return ALLOCATION_ERROR;}

dans votre code, vous perdez franchement votre temps. Les chances d'atteindre un point d'épuisement de la mémoire dans un système d'exploitation moderne sont faibles et les chances que vous fassiez quelque chose d'utile après que cela se soit passé sont à peu près nulles. Vous ferez bien mieux de simplement dire:

Band * newBand = new Band ( bandID, price - priceOffset, votes );

ot possible:

Band newBand( bandID, price - priceOffset, votes );

et en oubliant le traitement des exceptions dans ce cas.

Vous avez écrit un commentaire:

  

J'ai envisagé cela, mais la fonction fait partie de Tree.h et je l'inclue. La fonction définie est la suivante: template void Tree :: add (T & Et newData); Nous l'appelons de la manière suivante: priceTree.add (* newPriceNode); alors que priceTree est Tree, les deux étant définis dans le fichier cpp en question.

au lieu de:

priceTree.add(*newPriceNode);

essayez:

priceTree.add(newPriceNode); //no "*" before "newPriceNode"

add () prend une référence à un nœud, pas un pointeur sur un nœud (selon votre définition de Tree).

Vous obtenez des erreurs de liaison, pas des erreurs de compilateur. Cela nous indique que le compilateur savait quelle sorte de fonction Tree::add() était, mais n'avait pas de définition. Dans Tree.h, je vois une déclaration de la fonction add (), mais pas une définition. Cela me semble étrange; quelqu'un sait-il d'où Tree.h est originaire?

Habituellement, une classe de modèles contient des définitions de fonctions membres dans le fichier include, car les fonctions doivent être instanciées quelque part, et le plus simple est que le compilateur instancie lorsqu'il est utilisé et laisse l'éditeur de liens le trier. Si les définitions sont dans Tree.h, je pense que tout fonctionnera comme prévu.

Donc, je vais aller de l'avant et suggérer que les définitions sont dans un fichier séparé, non lié, et qu'il existe des dispositions ailleurs pour l'instanciation de types de base tels que Tree<int>. C’est probablement pour rationaliser la compilation, car normalement, ces éléments sont compilés à plusieurs endroits, ce qui prend du temps.

Dans ce cas, vous devez rechercher où Tree<Band> est instancié et ajouter une instanciation pour votre classe.

Je pourrais être très éloigné de la base, mais mon explication correspond aux faits que vous avez donnés.

Modifier après les premiers commentaires :

Les modèles sont un peu plus compliqués que les fonctions ordinaires, ce qui n’est généralement pas un problème. Si les définitions de tous les appels étaient dans Tree.h, alors Festival.cpp pourrait instancier Tree<Band>::add() et tout serait cool. C'est la technique habituelle et vous rencontrez ce problème car vous ne l'utilisez pas.

Lorsque vous écrivez une fonction, celle-ci est compilée et l'éditeur de liens la trouvera. Toute routine appelant cette fonction doit connaître le prototype de la fonction afin de savoir comment l'appeler. Lorsque vous écrivez un modèle, vous n'écrivez rien qui ira directement dans le programme, mais toute utilisation du modèle équivaut à écrire toutes les fonctions.

Par conséquent, il faut utiliser Tree<T>::add quelque part dans votre programme pour qu'une fonction Tree<T> soit compilée. La définition de Band doit être disponible pour le compilateur lorsque <=> est instancié, car sinon le compilateur n'a aucune idée de ce qu'il doit compiler. Dans ce cas, l’appel de la fonction est généré, vous avez l’assurance que la fonction est compilée ailleurs.

Par conséquent, vous devez instancier <=> à l'intérieur d'un fichier ayant accès aux définitions de <=> et de <=>. Cela signifie probablement un fichier qui est, ou inclut, Tree.cpp et comprend Festival.h.

L'éditeur de liens utilise déjà Tree.cpp, mais Tree.cpp n'a pas <=> défini, ce qui n'a donc aucun sens pour l'éditeur de liens. Les modèles ne sont utiles que pour le compilateur, et l'éditeur de liens ne fonctionne que sur ce que le compilateur a généré à partir de modèles.

La solution rapide consiste à prendre les définitions de Tree.cpp et à les placer dans Tree.h. Cela augmentera malheureusement les temps de compilation et de liaison. L’autre technique consiste à instancier tous les modèles utilisés dans Tree.cpp afin qu’ils soient compilés ici.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top