0

我收到以下类型的链接错误:

Festival.obj:错误LNK2019:函数__catch$中引用的未解析外部符号“public:void __thiscall Tree::add(class Price &)”(?add@?$Tree@VPrice@@@@QAEXAAVPrice@@@Z)? AddBand@Festival@@QAE?AW4StatusType@@HHH@Z$0

我曾经认为它与 try-catch 机制有关,但后来被告知并非如此。这是问题的更新版本。

我正在使用 Visual Studio 2008,但我在 g++ 中有类似的问题。

相关代码:

在 Festival.cpp

#include "Tree.h"
#include <exception>

using namespace std;

class Band{
public:
    Band(int bandID, int price, int votes=0): bandID(bandID), price(price), votes(votes){};
...
private:
...

};

class Festival{
public: 
    Festival(int budget): budget(budget), minPrice(0), maxNeededBudget(0), priceOffset(0), bandCounter(0){};
    ~Festival();
    StatusType AddBand(int bandID, int price, int votes=0);
    ...

private: 
    Tree<Band> bandTree;
    ...

};

StatusType Festival::AddBand(int bandID, int price, int votes){
    if ((price<0)||(bandID<0)){
        return INVALID_INPUT;
    }
    Band* newBand=NULL;
    try{
        newBand=new Band(bandID,price-priceOffset,votes);
    }
    catch(bad_alloc&){return ALLOCATION_ERROR;}
    if (bandTree.find(*newBand)!=NULL){
        delete newBand;
        return FAILURE;
    }
bandTree.add(*newBand);
....
}

在 Tree.h 中:

template<class T>
class Tree{
public:
    Tree(T* initialData=NULL, Tree<T>* initialFather=NULL);
    void add(T& newData);
....
private:
....
};

有趣的是,当类型 T 是像 int 这样的原始类型时,当我尝试使用 Tree 函数时,我没有出现链接错误。

4

6 回答 6

2

有 Tree.cpp 吗?如果有,也许你忘了链接它?Tree::add 的实现在哪里?此外,我看不到您在哪里调用 Tree::add。我想它应该在 try 语句中,就在 new 之后?

只是提醒:

对于大多数编译器(即那些进行单独编译的编译器),模板类的成员函数的实现必须在使用模板类的源文件的编译期间可见。通常人们通过将成员函数的实现放在头文件中来遵循此规则。

也许 Tree::add 不在标题内?那么在讨论的情况下,一个可能的解决方案是将 Tree::add 实现放在头文件中。

常规类和模板类之间存在差异,因为模板类不是“真正的”类——嗯,它是一个模板。如果您将 Tree 类定义为常规类,编译器可能会立即使用您的代码。在模板的情况下,编译器首先为您“编写”真正的类,用您提供的类型替换模板参数。现在,编译器将 cpp 文件一一编译。他不知道其他 cpp 文件,并且不能使用其他 cpp 文件中的任何内容。假设您的 Tree:add 实现如下所示:

void Tree::add(T& newData)
{
    newData.destroyEverything();
}

只要你的 T 有方法 destroyEverything,它就是完全合法的。当编译器编译 Class.cpp 时,它想确保您不对 T 做任何它不知道的事情。例如Tree<int>不会工作,因为 int 没有destroyEverything。编译器将尝试使用 int 而不是 T 编写代码,并发现代码无法编译。但由于编译器只“看到”当前 cpp 及其包含的所有内容,因此它无法验证 add 函数,因为它位于单独的 cpp 中。

不会有任何问题

void Tree::add(int& newData)
{
    newData.destroyEverything();
}

在单独的 cpp 中实现,因为编译器知道 int 是唯一可接受的类型,并且可以“依靠自己”,当他开始编译 Tree.cpp 时,他会发现错误。

于 2009-05-07T14:26:10.230 回答
0

您确定try/catch与它有什么关系吗?如果您简单地注释掉trycatch行,保留其余代码原样并构建它,会发生什么?

可能只是您缺少Tree::add(class Price &)从链接行定义的库。

于 2009-05-07T12:37:10.163 回答
0

更新:使用原始类型的树函数不会导致链接错误。我根据所说的一些事情更新了我的问题。

于 2009-05-07T14:22:46.197 回答
0

您收到链接错误,而不是编译器错误。这告诉我们编译器知道函数Tree::add()是什么类型,但没有定义。在 Tree.h 中,我看到了 add() 函数的声明,但没有看到定义。我觉得这很奇怪;有人知道 Tree.h 来自哪里吗?

通常一个模板类在包含文件中带有成员函数定义,因为函数必须在某个地方实例化,最简单的事情是编译器在使用时实例化并让链接器对其进行排序。如果定义在 Tree.h 中,我希望一切都按计划进行。

因此,我将冒险并建议将定义放在单独的文件中,而不是链接到其中,并且在其他地方有用于实例化基本类型(如Tree<int>. 这大概是为了简化编译,因为通常这些东西是在多个地方编译的,这需要时间。

在这种情况下,您需要做的是找到Tree<int>实例化的位置,并为您的类添加实例化。

我在这里可能有点离谱,但我的解释确实符合你给出的事实。

在第一条评论后编辑

模板比普通函数要复杂一些,这通常不是真正的问题。如果所有调用的定义都在 Tree.h 中,那么 Festival.cpp 将能够实例化Tree<Band>,一切都会很酷。这是通常的技术,你遇到这个问题是因为你没有使用它。

当你编写一个函数时,它会被编译,链接器会找到它。任何调用该函数的例程都需要知道函数原型,因此它将知道如何调用它。当您编写模板时,您不会编写任何直接进入程序的内容,但对模板的任何使用都算作编写所有函数。

因此,必须Tree<Band>在程序中的某个地方使用一些函数,才能Tree<Band>::add()编译出一个函数。的定义Tree<T>::add必须在实例化时对编译器可用Tree<Band>,否则编译器不知道要编译什么。在这种情况下,它正在生成函数调用,确信您将确保该函数在其他地方编译。

Tree<Band>因此,您必须在可以访问 和 的定义的文件中进行实例Tree<T>Band。这可能意味着一个文件是或包含 Tree.cpp 并包含 Festival.h。

链接器已经在使用 Tree.cpp,但是 Tree.cpp 并没有在Tree<Band>其中定义,所以它对链接器来说毫无意义。模板仅对编译器有用,链接器仅对编译器从模板生成的内容进行操作。

解决这个问题的快速方法是从 Tree.cpp 中获取定义并将它们放入 Tree.h。不幸的是,这可能会增加编译和链接时间。另一种技术是在 Tree.cpp 中实例化所有使用的模板,以便在那里编译它们。

于 2009-05-07T14:42:05.060 回答
0

正如其他人所说,您需要展示 Treee::add() 的实现并告诉我们您如何链接它。

在不相关的一点上,如果您使用的结构如下:

  Band* newBand=NULL;
    try{
        newBand=new Band(bandID,price-priceOffset,votes);
    }
    catch(bad_alloc&){return ALLOCATION_ERROR;}

在您的整个代码中,坦率地说,您是在浪费时间。在现代操作系统中,您达到内存耗尽点的可能性很小,并且您在它发生后做任何有用的事情的可能性几乎为零。你最好简单地说:

Band * newBand = new Band ( bandID, price - priceOffset, votes );

不可能:

Band newBand( bandID, price - priceOffset, votes );

在这种情况下忘记了异常处理。

于 2009-05-07T14:50:44.977 回答
0

你在评论中写道:

我考虑过这一点,但该函数是 Tree.h 的一部分,我确实包含了它。定义的函数是:template void Tree::add(T& newData); 我们这样称呼它:priceTree.add(*newPriceNode); 而 priceTree 是 Tree,两者都在相关的 cpp 文件中定义。

代替:

priceTree.add(*newPriceNode);

尝试:

priceTree.add(newPriceNode); //no "*" before "newPriceNode"

add() 引用节点,而不是指向节点的指针(根据您对树的定义)。

于 2009-05-07T14:51:24.163 回答