C ++链接错误:使用模板化类的未定义符号

时间:2008-11-23 04:32:50

标签: c++ linker

我从一个我写的课程中得到了一些非常奇怪的链接错误。我完全找不到任何可以描述正在发生的事情的东西。

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()”,引用于:player.o中的:PlayerList :: ~PlayerList()

头文件: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 个答案:

答案 0 :(得分:14)

在.cpp文件中定义模板时,必须使用所有类型/模板参数显式实例化它,模板将事先使用(将其放在.cpp文件中):

template class TreeNode<Player>;

如果您不知道将使用哪个模板参数,则必须将所有定义放入标题中。像

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

原因是当您要从某个地方使用模板时,编译器必须能够为模板的特定实例化生成代码。但是如果将代码放入.cpp文件并进行编译,编译器就无法获取代码来生成实例化(除非使用臭名昭着的export关键字,否则只支持只有很少的编译器。)

这也是我的C ++陷阱答案中的一个条目:What C++ pitfalls should I avoid?

答案 1 :(得分:2)

你是在宣告〜TreeNode(),但是你定义它了吗?

当您声明析构函数时,您停止编译器为您生成一个,但您必须在某处定义它,即使它是空的。

如果你想要一个空的析构函数,你有两个选择:

- 完全删除〜TreeNode()的声明,并依赖自生成的析构函数 - 将其定义为空。在这里,Inling非常好,IE。 ~SreeNode(){};

答案 2 :(得分:1)

编译器抱怨没有找到析构函数的实现。如前所述,如果您确实声明了析构函数,编译器将不会自动为您生成一个。

根据David Reis建议删除或提供一个空的析构函数,我显然会选择第二个。如果你的类是派生的(你有虚拟方法)那么你应该提供一个虚拟析构函数,即使它是空的(同样适用于BaseNode)。

如果您依赖于编译器生成的版本,并且用户代码通过指向其构造函数不是虚拟的基类的指针删除派生对象,则不会调用派生对象的析构函数,可能会泄漏资源。

答案 3 :(得分:0)

您是否忘记链接包含类函数的函数体的目标文件?

E.g。你在.cpp中有这样的东西:

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

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