6

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

视觉工作室 (Windows XP)

player.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()”,引用自:players.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

4 回答 4

15

当您在 .cpp 文件中定义模板时,您必须使用所有已知的类型/模板参数显式实例化它,该模板将像这样预先使用(将其放在 .cpp 文件中):

template class TreeNode<Player>;

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

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

原因是当您要从某个地方使用模板时,编译器必须能够为模板的特定实例化生成代码。但是如果你把代码放到一个 .cpp 文件中并编译它,编译器就没有办法得到它的代码来生成实例化(除了使用臭名昭著的export关键字时,只有极少数编译器支持) .

这也是我的 C++ 陷阱答案中的一个条目:我应该避免哪些 C++ 陷阱?

于 2008-11-23T11:18:01.043 回答
2

好吧,您正在声明 ~TreeNode(),但您是在定义它吗?

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

如果您的意图是有一个空的析构函数,您有两个选择:

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

于 2008-11-23T04:53:49.227 回答
1

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

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

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

于 2008-11-23T10:33:01.837 回答
0

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

例如,你会在 .cpp 中有这样的内容:

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

TreeNode::~TreeNode()
{
    // ...
}
于 2008-11-23T04:44:44.037 回答