2010-07-23 5 views
3

Donc j'essaye de choisir C++, et pour cela j'ai décidé d'écrire une classe Group générique en utilisant des templates, qui prend un Type et une taille comme paramètre template:g ++ erreur de symbole en double lors de l'utilisation de templates (noob question)

dans group.h:

#ifndef __GROUP_H 
#define __GROUP_H 
#define MAX_SIZE 10 

/********************************************************** 
* Define a Group class that handles a collection of members 
* of some Type 
**********************************************************/ 
template <class Type, int max> 
class Group { 
    private: 
     std::string name; 
     int count, size; 
     Type * members[max]; 

    public: 
     Group(); 
     Group(const std::string & _name); 
     ~Group(); 

     // display instance info 
     virtual void show(); 

     // add member 
     void add_member(Type &); 

     // list memebers 
     void list(); 

     // name setter/getter 
     void set_name(const std::string &); 
     const std::string & get_name(); 

}; 

#endif 

group.cc:

/********************************************************** 
* class methods for Group 
**********************************************************/ 
template <class Type, int max> 
Group<Type, max>::Group() : count(0), size(max), name("New Group") {}; 

template <class Type, int max> 
Group<Type, max>::Group(const std::string & _name) : name(_name), count(0), size(max) {}; 

template <class Type, int max> 
Group<Type, max>::~Group() { 
    int i = 0; 
    while(i < this->count) { 
    delete this->members[i]; 
    ++i; 
    } 
} 

template <class Type, int max> 
void Group<Type, max>::show() { 
    std::cout << "<#Group - Name: " << this->name << ", Members: " << this->count << "/" << this->size << " >\n"; 
} 

template <class Type, int max> 
void Group<Type, max>::add_member(Type & member) { 
    if (this->count < this->size) { 
     this->members[this->count] = &member; 
     this->count++; 
    } else { 
     std::cout << "Error - this Group is full!\n"; 
    } 
} 

template <class Type, int max> 
void Group<Type, max>::list() { 
    int i = 0; 
    std::cout << "The following are members of the Group " << this->name <<":\n"; 
    // assumes the member has a show() method implemented 
    while (i < this->count) { 
     std::cout << i << ". "; 
     (this->members[i])->show(); 
     ++i; 
    } 
} 

template <class Type, int max> 
void Group<Type, max>::set_name(const std::string & _name) { 
    this->name = _name; 
} 

template <class Type, int max> 
const std::string & Group<Type, max>::get_name() { 
    return this->name; 
} 

J'ai aussi implémenté une classe Person et une classe Employee (qui hérite de Person) et les deux fonctionnent et ont la méthode show().

Mes principaux ressemble à ceci:

test.cc

#include <iostream> 
#include "group.h" // this also has the declarations and implementation for Person/Employee 

int main (int argc, char const *argv[]) 
{ 
    // Person ctor takes name and age 
    Person p1("John", 25); 
    Person p2("Jim", 29); 

    // Group takes name to init 
    Group <Person, 5> g("Ozcorp"); 
    g.add_member(p1); 
    g.add_member(p2); 
    g.list();  
} 

Je Compilé avec un Makefile simple:

test: test.cc group.o 
    g++ -o test test.cc group.o 

group.o: group.h group.cc 
    g++ -c group.cc 

Et enfin (ouf), quand je courais avec ./test ont les erreurs suivantes:

Undefined symbols: 
    "Group<Person, 5>::list()", referenced from: 
     _main in ccaLjrRC.o 
    "Group<Person, 5>::Group(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)", referenced from: 
     groups() in ccaLjrRC.o 
     _main in ccaLjrRC.o 
    "Group<Person, 5>::~Group()", referenced from: 
     groups() in ccaLjrRC.o 
     _main in ccaLjrRC.o 
     _main in ccaLjrRC.o 
    "Group<Person, 5>::add_member(Person&)", referenced from: 
     _main in ccaLjrRC.o 
     _main in ccaLjrRC.o 
ld: symbol(s) not found 
collect2: ld returned 1 exit status 
make: *** [test] Error 1 

Si vous l'avez fait jusqu'ici - merci - et j'apprécierais un peu de savoir pourquoi cela se produit. J'ai essayé de partager autant que possible du code (évidemment), alors pardonnez-moi si c'est beaucoup de choses. La source a été compilée avec g ++ 4.2.1 sur mac osx 10.6.4. En outre, tout style/bonnes astuces de codage-habitudes seront appréciés. Merci!

+0

Les macros avec un trait de soulignement principal (ou deux) sont réservées à l'implémentation. Utilisez simplement GROUP_H dans votre cas, rien de mal à cela. – rubenvb

Répondre

5

La définition de membre de classe modèle ne va pas aux fichiers .cc/.cpp. Ils vont dans le .h, ou dans un fichier .hpp/.hxx inclus par le .h Le rationnel est que les fichiers .cc/.cpp sont utilisés pour construire des fichiers objets. Toutefois, avec le code basé sur un modèle, les objets ne peuvent pas être créés avant que les arguments basés sur un modèle ne soient substitués. Par conséquent, leur implémentation doit être disponible pour tous les morceaux de code en les instanciant: dans un fichier .h like.

3

Le compilateur doit voir les définitions de modèle s'il veut les instancier. Le moyen le plus simple d'y parvenir est de placer chaque partie de code (y compris toutes les définitions) dans le fichier d'en-tête.

+2

+1, et rappelez-vous que si les définitions de modèle sont dans l'en-tête, elles doivent être en ligne (mot-clé 'inline' ou les définir dans la définition de classe) si vous ne voulez pas passer de 'symbole non trouvé' 'type d'erreurs. –

+0

Cela fonctionne, même si je pensais que l'idée de séparer l'en-tête du fichier d'implémentation (créer group.h et group.cc) était de montrer l'API à l'utilisateur (ie - les déclarations de fonction) tout en pouvant masquer leur implémentation fournir le fichier obj * .o/*. Alors, comment cela est-il possible si j'inclus tout (déclaration + implémentation) dans le fichier .h? – sa125

+1

@ sa125: Voir ma réponse à propos de .hxx: les définitions peuvent être exportées dans un fichier d'en-tête supplémentaire, inclus par le fichier principal .h, masquant l'implémentation. – Scharron

3

L'instanciation de modèle peut être soit implicite (exécutée par le compilateur), soit explicite (demandée par l'utilisateur). Pour les instanciations implicites, le compilateur doit voir la définition de la fonction du modèle à l'endroit de l'appel - ce qui, comme l'a déjà dit Philipp -, est plus facile en ajoutant les définitions de fonctions du modèle dans le fichier d'en-tête.

L'alternative est en quelque sorte limitée: si vous connaissez toutes les instanciations de votre modèle initial, vous pouvez laisser les définitions de fonction de modèle ailleurs et les instancier explicitement. Dans votre cas, à la fin de group.cpp vous pouvez ajouter:

template class Group<Person, 5>; 

L'avantage (si de nombreuses parties de l'utilisation de code ce modèle et il est coûteux à instancier) est que le code du modèle sera compilé uniquement une fois dans une unité de traduction et plus tard liée, ce qui peut réduire le temps de compilation, mais l'énorme inconvénient est que l'ensemble des instanciations valides doit être connu à l'avance et le modèle ne peut pas être utilisé avec d'autres types. .En cas de doute, indiquez les définitions dans le fichier d'en-tête et rappelez-vous que les définitions de fonctions doivent être en ligne, sinon vous rencontrerez l'erreur de lien inverse: "duplicate symbol" si vous incluez l'en-tête des différentes unités de traduction. Une fonction membre est insérée par défaut si elle est définie dans les accolades de classe ou si elle a le mot clé inline. N'utilisez PAS __SOMETHING comme protection d'en-tête.

Questions connexes