Ошибки связывания C ++: неопределенные символы, использующие шаблонный класс - PullRequest
6 голосов
/ 23 ноября 2008

Я получаю некоторые действительно странные ошибки связывания из класса, который я написал. Я совершенно не могу найти что-нибудь, что будет описывать происходящее.

Visual Studio (Windows XP)

Players.obj: ошибка LNK2019: неразрешенный внешний символ "public: __thiscall TreeNode :: TreeNode (void)" (?? 0? $ TreeNode @ VPlayer @@@@ QAE @ XZ), на который ссылается функция "public: __thiscall PlayerList :: PlayerList (void) "(?? 0PlayerList @@ QAE @ XZ)

Xcode (OSX 10.5)

Неопределенные символы: «TreeNode :: ~ TreeNode ()», на который ссылается: PlayerList :: ~ PlayerList () в Players.o

Заголовочный файл: 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;
};

Код из подкласса TreeNode

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

Ответы [ 4 ]

14 голосов
/ 23 ноября 2008

Когда вы определяете свой шаблон в файле .cpp, вы должны явно создать его экземпляр со всеми известными типами / параметрами шаблона, так как шаблон будет использоваться заранее, вот так (поместите его в файл .cpp):

template class TreeNode<Player>;

Если вы не знаете, с какими параметрами шаблона будет использоваться шаблон, вы должны поместить все определения в заголовок. как

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

Причина в том, что когда вы собираетесь использовать шаблон откуда-то, компилятор должен иметь возможность генерировать код для этого конкретного экземпляра шаблона. Но если вы поместите код в файл .cpp и скомпилируете его, у компилятора не будет возможности получить код для генерации экземпляров (кроме случаев использования печально известного ключевого слова export, которое поддерживается только очень несколько компиляторов).

Это также запись в моем ответе на C ++ Pitfalls: Каких ошибок C ++ мне следует избегать?

2 голосов
/ 23 ноября 2008

Хорошо, вы объявляете ~ TreeNode (), но вы его определяете?

Когда вы объявляете деструктор, вы не позволяете компилятору генерировать его для вас, но вы должны определить его где-то, даже если он пуст.

Если ваше намерение состояло в том, чтобы иметь пустой деструктор, у вас есть два варианта:

-Удалить объявление ~ TreeNode () полностью, и полагаться на сам генерируемый деструктор Определите это как пустое. Инлинг был бы очень хорош здесь, IE. ~ TreeNode () {};

1 голос
/ 23 ноября 2008

Компилятор жалуется, что не нашел реализацию деструктора. Как указывалось ранее, если вы объявите деструктор, компилятор не сгенерирует его автоматически.

По предложению Дэвида Рейса об удалении или предоставлении пустого деструктора, я бы явно пошел на второй. Если ваш класс должен быть производным (у вас есть виртуальные методы), вы должны предоставить виртуальный деструктор, даже если он пустой (то же самое относится и к BaseNode).

Если вы зависите от версии, сгенерированной компилятором, и пользовательский код удаляет производный объект через указатель на базовый класс, конструктор которого не является виртуальным, то деструктор производного объекта не будет вызываться, возможно утечка ресурсов.

0 голосов
/ 23 ноября 2008

Вы забыли связать объектный файл, содержащий тела функций для функций вашего класса?

например. у вас будет что-то вроде этого в .cpp:

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

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