-1

このプログラムについて:

プログラムは HashTable を実現するためのものです。

問題は次のとおりです。

1>正在链接... 1>HashTable.obj : error LNK2019: 无法解析的外部シンボル "public: int __thiscall HashTable::remove(class Employee const &)" (?remove@?$HashTable@VEmployee@@@@ QAEHABVEmployee@@@Z),この記号は関数 _wmain で参照されています

1>HashTable.obj : エラー LNK2019: 无法解析的外部シンボル "public: int __thiscall HashTable::contains(class Employee const &)" (?contains@?$HashTable@VEmployee@@@@QAEHABVEmployee@@@Z),このシンボルは関数 _wmain で参照されています

1>HashTable.obj : error LNK2019: 无法解析的外部シンボル "public: void __thiscall HashTable::display(void)const " (?display@?$HashTable@VEmployee@@@@QBEXXZ),このシンボルは関数 _wmain 中被引用 1>HashTable.obj : error LNK2019: 无法解析的外部シンボル "public: int __thiscall HashTable::insert(class Employee const &)" (?insert@?$HashTable@VEmployee@@@@QAEHABVEmployee@@@Z ),このシンボルは関数 _wmain で参照されています

1>HashTable.obj : error LNK2019: 无法解析的外部シンボル "public: __thiscall HashTable::HashTable(int)" (??0?$HashTable@VEmployee@@@@QAE@H@Z),このシンボルは関数内_wmain中引用

質問:

Hash.hで宣言し、Hash.cppで定義したため、なぜこれが起こるのかわかりません。この質問を投稿する前に、インターネットで回答を検索しましたが、欲しい回答がありません。テンプレートを誤用しているときにテンプレートを使用する特別な方法はありますか?

Hash.h:

#include "stdafx.h"
#include <iostream>
#include <vector>
#include <list>
#include <string>
#include <cstdlib>
#include <cmath>
#include <algorithm>

using namespace std;

template <class T>
class HashTable
{
    public:
        HashTable(int size = 101);
        int insert(const T& x);
        int remove(const T& x);
        int contains(const T& x);
        void make_empty();
        void display()const;
    private:
        vector<list<T> > lists;
        int currentSize;
        int hash(const string& key);
        int myhash(const T& x);
        void rehash();
};

class Employee
{
    public:
        Employee(){}
        Employee(const string n,int s=0):name(n),salary(s){ }
        const string & getName()const { return name; } 
        
        bool operator == (const Employee &rhs) const
        {
            return getName() == rhs.getName();
        }
        
        bool operator != (const Employee &rhs) const
        {
            return !(*this == rhs);
        }
        
        friend ostream& operator <<(ostream& out,const Employee& e)
        {
            out<<"("<<e.name<<","<<e.salary<<") ";
            return out;
        }
    
    private:
        string name;
        int salary;
};

ハッシュ.cpp

#include "stdafx.h"
#include "Hash.h"

int nextPrime(const int n);

template <class T> HashTable<T>::HashTable(int size)
{
    lists = vector<list<T> >(size);
    currentSize = 0;
}

template <class T> int HashTable<T>::hash(const string& key)
{ 
    int hashVal = 0;
    int tableSize = lists.size();
    for(int i=0;i<key.length();i++)
        hashVal = 37*hashVal+key[i];
    hashVal %= tableSize;
    if(hashVal < 0)
        hashVal += tableSize;
    return hashVal;
}

template <class T> int HashTable<T>:: myhash(const T& x)
{
    string key = x.getName();
    return hash(key);
}

template <class T> int HashTable<T>::insert(const T& x)
{
    list<T> &whichlist = lists[myhash(x)];
    if(find(whichlist.begin(),whichlist.end(),x) != whichlist.end())
        return 0;
    whichlist.push_back(x);
    currentSize = currentSize + 1;
    if(currentSize > lists.size())
        rehash();
    return 1;
}

template <class T> int HashTable<T>::remove(const T& x)
{
    typename std::list<T>::iterator iter; list<T> &whichlist = lists[myhash(x)];
    iter = find(whichlist.begin(),whichlist.end(),x);
    if( iter != whichlist.end())
    {
        whichlist.erase(iter); 
        currentSize--;
        return 1;
    }
    return 0;
}

template <class T> int HashTable<T>::contains(const T& x)
{
    list<T> whichlist;
    typename std::list<T>::iterator iter;
    whichlist = lists[myhash(x)];
    iter = find(whichlist.begin(),whichlist.end(),x);
    if ( iter != whichlist.end())
        return 1;
    return 0;
}

template <class T> void HashTable<T>::make_empty()
{
    for(int i=0;i<lists.size();i++)
        lists[i].clear();
    currentSize = 0;
    return 0;
}

template < class T > void HashTable < T >::rehash()
{
    vector < list < T > >oldLists = lists;
    lists.resize(nextPrime(2 * lists.size()));
    for (int i = 0; i < lists.size(); i++)
    lists[i].clear();
    currentSize = 0;
    for (int i = 0; i < oldLists.size(); i++) {
    typename std::list < T >::iterator iter = oldLists[i].begin();
    while (iter != oldLists[i].end())
        insert(*iter++);
    }
}

template < class T > void HashTable < T >::display() const const
{
    for (int i = 0; i < lists.size(); i++) {
    cout << i << ": ";
    typename std::list < T >::const_iterator iter = lists[i].begin();
    while (iter != lists[i].end()) {
        cout << *iter << " ";
        ++iter;
    }
    cout << endl;
    }
}

int nextPrime(const int n)
{
    int ret, i;
    ret = n;
    while (1) {
    int flag = 1;
    for (i = 2; i < sqrt((float) ret); i++)
        if (ret % i == 0) {
        flag = 0;
        break;
        }
    if (flag == 1)
        break;
    else {
        ret = ret + 1;
        continue;
    }
    }
    return ret;
}

ハッシュテーブル.cpp:

// HashTable.cpp :
// #include "stdafx.h"

#include "Hash.h"
int _tmain(int argc, _TCHAR * argv[])
{
    Employee e1("Tom", 6000);
    Employee e2("Anker", 7000);
    Employee e3("Jermey", 8000);
    Employee e4("Lucy", 7500);
    HashTable < Employee > emp_table(13);
    emp_table.insert(e1);
    emp_table.insert(e2);
    emp_table.insert(e3);
    emp_table.insert(e4);
    cout << "Hash table is: " << endl;
    emp_table.display();
    if (emp_table.contains(e4) == 1)
    cout << "Tom is exist in hash table" << endl;
    if (emp_table.remove(e1) == 1)
    cout << "Removing Tom form the hash table successfully" << endl;
    if (emp_table.contains(e1) == 1)
    cout << "Tom is exist in hash table" << endl;
    else
    cout << "Tom is not exist in hash table" << endl;
    return 0;
}

4

2 に答える 2

2

テンプレートを誤用している間、テンプレートを使用する際にいくつかの特別な方法があるのは理由ですか?

正確に。テンプレート クラス/関数の定義は、それが使用されるすべての翻訳単位でアクセスできる必要があります。つまり、その宣言をその定義から分離することはできません。この制限を回避するには、テンプレートをインラインで、つまりヘッダー ファイルで定義します。

ドラフト n3485 の引用

[temp] パラグラフ 1

関数テンプレート、クラス テンプレートのメンバー関数、またはクラス テンプレートの静的データ メンバーは、対応する特殊化が明示的にインスタンス化されない限り [...] 暗黙的にインスタンス化されるすべての翻訳単位で定義されなければなりません [...]翻訳単位; 診断は必要ありません。

于 2013-09-13T01:36:24.510 に答える
1

テンプレートはクラスでも関数でもありません。テンプレートは、コンパイラがクラスまたは関数のファミリを生成するために使用する「パターン」です。

コンパイラがコードを生成するためには、テンプレート定義 (宣言だけでなく) と特定の型/テンプレートを「埋める」ために使用されるものの両方を確認する必要があります。たとえば、Foo を使用しようとしている場合、コンパイラは、Foo テンプレートと、特定の Foo を作成しようとしているという事実の両方を確認する必要があります。

コンパイラは、別の .cpp ファイルをコンパイルしている間、おそらく 1 つの .cpp ファイルの詳細を覚えていません。できますが、ほとんどの場合はできません。この FAQ を読んでいる場合は、ほぼ確実にできません。ところで、これは「個別コンパイル モデル」と呼ばれます。

  • このFAQをお読みください。中国語版はこちら

  • 標準using namespace std;から使用したい場合は、ヘッドファイルに書き込まないでください。代わりに書き込みます。stringstd::string

  • コンストラクターにはパラメーターが 1 つしかない場合、explicit キーワードを使用して暗黙的な変換を回避できます。
于 2013-09-13T01:38:44.233 に答える