0

次のタイプのリンケージ エラーが発生します。

Festival.obj : エラー LNK2019: 未解決の外部シンボル "public: void __thiscall Tree::add(class Price &)" (?add@?$Tree@VPrice@@@@QAEXAAVPrice@@@Z) 関数 __catch$ で参照されています? 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ステートメント内にあるべきだと思いますか?

ただのリマインダー:

ほとんどのコンパイラ(つまり、個別のコンパイルを実行するコンパイラ)の場合、テンプレートクラスのメンバー関数の実装は、テンプレートクラスを使用するソースファイルのコンパイル中に表示される必要があります。通常、メンバー関数の実装をヘッダーファイル内に配置することで、このルールに従います。

たぶんTree::addはヘッダーの中にありませんか?次に、説明したケースで考えられる解決策は、ヘッダーファイル内にTree::add実装を配置することです。

通常のクラスとテンプレートクラスの違いは、テンプレートクラスが「実際の」クラスではないために存在します。つまり、テンプレートです。Treeクラスを通常のクラスとして定義した場合、コンパイラーはコードをすぐに使用できたはずです。テンプレートの場合、コンパイラは最初に実際のクラスを「書き込み」、テンプレートパラメータを指定したタイプに置き換えます。現在、コンパイラはcppファイルを1つずつコンパイルします。彼は他のcppファイルを認識しておらず、他のcppファイルからは何も使用できません。Tree:addの実装が次のようになっているとしましょう。

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

あなたのTがメソッドdestroyEverythingを持っている限り、それは完全に合法です。コンパイラがClass.cppをコンパイルするとき、それが知らないことをTで行わないようにしたいのです。たとえばTree<int>、intにはdestroyEverythingがないため、機能しません。コンパイラーは、Tではなくintを使用してコードを書き込もうとし、コードがコンパイルされないことを検出します。ただし、コンパイラは現在のcppとそれに含まれるすべてのもののみを「認識」するため、別のcppにあるため、add関数を検証できません。

何の問題もありません

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

コンパイラはintが唯一の受け入れ可能な型であり、Tree.cppをコンパイルするときにエラーを見つけることを「自分自身に頼る」ことができることを知っているため、別のcppで実装されます。

于 2009-05-07T14:26:10.230 に答える
0

更新:プリミティブ型でTree関数を使用しても、リンクエラーは発生しません。私は言われたことのいくつかに照らして私の質問を更新しました。

于 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であるか、Tree.cppを含み、Festival.hを含むファイルを意味します。

リンカはすでにTree.cppを使用していますが、Tree.cppにはTree<Band>定義されていないため、リンカには意味がありません。テンプレートはコンパイラーにのみ役立ち、リンカーはコンパイラーがテンプレートから生成したものに対してのみ動作します。

これを解決する簡単な方法は、Tree.cppから定義を取得し、それらをTree.hに配置することです。残念ながら、これによりコンパイルとリンクの時間が長くなる可能性があります。もう1つの手法は、Tree.cppで使用されるすべてのテンプレートをインスタンス化して、そこでコンパイルされるようにすることです。

于 2009-05-07T14:42:05.060 に答える
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 に答える
0

他の人が述べているように、Treee::add() の実装を示し、それをどのようにリンクしているかを教えてください。

無関係な点で、次のような構成を使用している場合:

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

コード全体を通して、率直に言って時間を無駄にしています。最新の OS でメモリが枯渇する可能性はごくわずかであり、それが発生した後に何か役に立つことを行う可能性はほぼゼロです。単純に次のように言う方がはるかに良いでしょう:

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

またはおそらく:

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

この場合、例外処理を忘れています。

于 2009-05-07T14:50:44.977 に答える
0

try/はそれと関係がありますかcatch? try行と行を単にコメントアウトしcatch、残りのコードをそのままにしてビルドするとどうなりますか?

Tree::add(class Price &)リンク行から定義するライブラリが不足している可能性があります。

于 2009-05-07T12:37:10.163 に答える