抛出导致链接错误

时间:2009-05-07 12:29:57

标签: c++ visual-studio-2008 templates try-catch linker-errors

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

  

Festival.obj:错误LNK2019:   未解决的外部符号“public:   void __thiscall Tree :: add(class Price&)“   (?添加@?$ @树@@@@ VPrice @@@ QAEXAAVPrice Z)   在函数中引用   __catch $?AddBand @ @@节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:
....
};

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

6 个答案:

答案 0 :(得分:2)

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

之后

提醒一下:

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

也许Tree :: add不在标题内?然后在讨论的案例中可能的解决方案是将Tree :: add实现放在头文件中。

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

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

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

不会有任何问题
void Tree::add(int& newData)
{
    newData.destroyEverything();
}

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

答案 1 :(得分:0)

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

可能只是因为您错过了从链接行定义Tree::add(class Price &)的库。

答案 2 :(得分:0)

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

答案 3 :(得分:0)

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

通常模板类在include文件中带有成员函数定义,因为函数必须在某处实例化,最简单的事情是编译器在使用时进行实例化并让链接器对其进行排序。如果定义在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中实例化所有模板使用,以便在那里编译它们。

答案 4 :(得分: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 );

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

答案 5 :(得分:0)

您在评论中写道:

  

我考虑过这个,但函数是Tree.h的一部分,我确实包含了它。定义的函数是:template void Tree :: add(T&amp; newData);我们用以下方式调用它:priceTree.add(* newPriceNode);而priceTree是Tree,两者都在相关的cpp文件中定义。

而不是:

priceTree.add(*newPriceNode);

尝试:

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

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