2008-11-23 10 views
6

Obtengo algunos errores de enlace realmente extraños de una clase que escribí. No puedo encontrar nada que describa lo que está sucediendo.Errores de vinculación de C++: símbolos no definidos que utilizan una clase de plantilla

Visual Studio (Windows XP)

players.obj: LNK2019 de error: símbolo externo sin resolver "público: __thiscall NodoArbol :: TreeNode (void)"? (?? 0 $ NodoArbol @ VPlayer @@@ @ QAE @ XZ) que se hace referencia en la función "pública: __thiscall playerlist :: playerlist (void)" (?? 0PlayerList @@ QAE @ XZ)

Xcode (OSX 10,5)

símbolos no definidos: "TreeNode :: ~ TreeNode()", a la que se hace referencia desde: Playerlist :: ~ playerlist() en players.o

del encabezado del archivo: generics.h

class TreeNode : public BaseNode{ 
public: 
    const static int MAX_SIZE = -1; //-1 means any size allowed. 
    const static int MIN_SIZE = 0; 
    //getters 
    int size() const; 
    vector<C*> getChildren() const; 
    //setters 
    void setChildren(vector<C*> &list); 
    //Serialization 
    virtual void display(ostream &out) const; 
    virtual void read(istream &in); 
    virtual void write(ostream &out) const; 
    //Overrides so SC acts like an array of sorts. 
    virtual C* get(int id) const; 
    virtual int get(C *child) const; 
    virtual bool has(C *child) const; 
    virtual C* pop(int id); 
    virtual void push(C *child); 
    virtual TreeNode& operator<< (C *child); //append 
    virtual void remove(int id); //Clears memory 
    virtual void remove(C *child); //Clears memory 
    //Initalizers 
    TreeNode(); 
    TreeNode(istream &in); 
    TreeNode(long id, istream &in); 
    TreeNode(BaseNode* parent, istream &in); 
    TreeNode(long id, BaseNode* parent); 
    TreeNode(long id, BaseNode* parent, istream &in); 
    ~TreeNode(); 
    string __name__() const{ return "TreeNode"; } 
protected: 
    void clearChildren(); 
    void initalizeChildren(); 
    vector<C*> _children; 
}; 

Código de una subclase de NodoArbol

PlayerList::PlayerList() : TreeNode<Player>(){} 
PlayerList::PlayerList(istream &in) : TreeNode<Player>(in){} 
PlayerList::~PlayerList(){} 
+0

¿Dónde está el código que define NodoArbol :: ~ NodoArbol()? –

+0

El código está en generics.cpp – epochwolf

Respuesta

14

Cuando define su plantilla en un archivo .cpp, tiene que crear una instancia explícita con todos los tipos/parámetros de plantilla conocidos, la plantilla se usará de antemano de esta manera (colóquela en.cpp):

template class TreeNode<Player>; 

Si usted no sabe con qué parámetros plantilla se utilizará la plantilla, usted tiene que poner todas las definiciones en la cabecera. como

template<typename T> 
class TreeNode { 
public: 
    TreeNode() /* now, also put the code into here: */ { doSomething(); } 
}; 

La razón es que cuando se va a utilizar la plantilla de algún lugar, el compilador tiene que ser capaz de generar el código para que ejemplificación específica de la plantilla. Pero si coloca el código en un archivo .cpp y lo compila, no hay forma de que el compilador tenga en sus manos el código para generar la creación de instancias (excepto cuando usa la infame palabra clave export, que solo es compatible con muy pocos compiladores)

Ésta es también una entrada en mi respuesta C++ trampas: What C++ pitfalls should I avoid?

+0

"no hay forma de que el compilador tenga en sus manos el código para generar la creación de instancias". A menos que use, p. Comeau C++, donde el enlazador es capaz de crear instancias de plantilla. La tecnología puede llegar a los compiladores convencionales, algún día, simplemente no hoy ... –

+0

hehe onebyone :) seamos optimistas todo el día –

+0

Por cierto, el compilador de inteligencia también es compatible con esto, en virtud de su interfaz de grupo de diseño fino edison. –

0

¿Está olvidando a vincular el objeto archivo que contiene los cuerpos de función para sus funciones de clase?

E.g. usted tendría algo como esto en un .cpp:

TreeNode::TreeNode() : 
    /* initializers here */ 
{ 
    // ... 
} 

TreeNode::~TreeNode() 
{ 
    // ... 
} 
2

Bien está declarando ~ NodoArbol(), pero estás definirlo?

Cuando declara el destructor, impide que el compilador genere uno para usted, pero debe definirlo en algún lugar, incluso si está vacío.

Si su intención era tener un destructor vacío tiene dos opciones:

-Eliminar la declaración de ~ NodoArbol() por completo, y se basan en la auto generada destructor -Definir como vacía. Inling sería muy agradable aquí, IE. ~ TreeNode() {};

+0

Falta '{}', después de mi constructor, doh! ¡Saludos por esto! –

1

El compilador se queja de no encontrar una implementación del destructor. Como se señaló anteriormente, si declara el destructor, el compilador no generará automáticamente uno para usted.

A la sugerencia por David Reis de eliminar o proporcionar un destructor vacío, claramente iré por el segundo. Si su clase debe ser derivada (tiene métodos virtuales), debe proporcionar un destructor virtual , incluso si está vacío (lo mismo se aplica a BaseNode).

Si depende de la versión generada del compilador y el código de usuario elimina un objeto derivado mediante un puntero a una clase base cuyo constructor no es virtual, no se invocará el destructor del objeto derivado, posiblemente con recursos que se escapan.

Cuestiones relacionadas