2011-12-06 20 views
0

&をコンパイルしようとしたときに悪名高い "未定義の参照"エラーが発生しました。C++:リンカーエラー:別のファイルで定義されている特定のクラスメンバーにのみ未定義の参照

正確なエラーメッセージ:

g++ -o main list.cpp main.cpp /tmp/ccv6M2I6.o: In function main': main.cpp:(.text+0x219): undefined reference to List::print() const'

main.cppに:

#include <iostream> 
#include "list.hpp" 
using namespace std; 


void printStats(IntList& l) { 
    cout << endl << "____________________" << endl; 
    cout << "Length: " << l.getCount() << endl; 
    cout << "Min: " << l.min() << endl; 
    cout << "Max: " << l.max() << endl; 
    cout << "Average: " << l.average(); 
    cout << endl << "____________________" << endl; 
} 

int main() { 
    IntList l = IntList(); 

    for(int i=1; i <= 10; i++) { 
     l.insert(i, l.getCount() - 1); // works fine 
    } 

    printStats(l); // works fine, too 
    l.print(); // causes the error 

    return 0; 
} 

面白いことがある:どちらもメンバー関数insert()も分()、MAX()または平均( )問題を引き起こします。それは単にprint()です。

[編集]:それはprint()だけでなく、remove()です。

list.hpp:

#ifndef __LIST_HPP__ 
#define __LIST_HPP__ 

template <typename T> 
class List { 
    public: 
     class OutOfBoundsException { }; 

     List(); 
     List(const List& l); 
     ~List(); 

     List& operator=(const List& l); 

     unsigned int getCount() const; 
     bool isEmpty() const; 
     void print() const; 
     void insert(T value, unsigned int position = 0); 
     void remove(unsigned int position); 
     T pop(unsigned int position = 0); 
     T getElement(unsigned int position) const; 

    protected: 
     // double linked list 
     struct dllist_entry { 
      T value; 
      dllist_entry* next; 
      dllist_entry* prev; 
     }; 

     dllist_entry* first; 
     dllist_entry* last; 

     unsigned int length; 

     void clear(); 

     dllist_entry* getElementRaw(unsigned int position) const; 
}; 

class IntList : public List<int> { 
    public: 
     IntList(); 
     IntList(const IntList& l); 
     ~IntList(); 

     IntList& operator=(const IntList& l); 

     int max() const; 
     int min() const; 
     float average() const; 
}; 


#endif 

list.cpp:

#include <iostream> 
#include "list.hpp" 
using namespace std; 


template <typename T> 
List<T>::List() { 
    this->first = NULL; 
    this->last = NULL; 
    this->length = 0; 
} 

template <typename T> 
List<T>::List(const List& l) { 
    this->first = NULL; 
    this->last = NULL; 
    this->length = 0; 

    for(unsigned int i=0; i < l.getCount(); i++) { 
     insert(l.getElement(i)); 
    } 
} 

template <typename T> 
List<T>& List<T>::operator=(const List<T>& l) { 
    if(this != &l) {  
     // Liste leeren 
     clear(); 

     for(unsigned int i=0; i < l.getCount(); i++) { 
      insert(l.getElement(i)); 
     } 
    } 

    return *this; 
} 

template <typename T> 
List<T>::~List() { 
    clear(); 
} 

template <typename T> 
void List<T>::clear() { 
    dllist_entry* iter = first; 
    dllist_entry* next; 

    while(iter != NULL) { 
     next = iter->next; 
     delete iter; 
     iter = next; 
    } 

    length = 0; 
} 

template <typename T> 
unsigned int List<T>::getCount() const { 
    return this->length; 
} 

template <typename T> 
bool List<T>::isEmpty() const { 
    return this->length == 0; 
} 

template <typename T> 
void List<T>::print() const { 
    // aus Performance-Gründen nicht getElement() benutzen 

    for(dllist_entry* iter = first; iter != NULL; iter = iter->next) { 
     cout << iter->value << endl; 
    } 
} 

template <typename T> 
void List<T>::insert(T value, unsigned int position) { 

    dllist_entry* new_one = new dllist_entry; 
    new_one->value = value; 

    if(getCount() > 0) { 
     if(position < getCount()) { 
      if(position == 0) { 
       new_one->prev = NULL; 
       new_one->next = first; 
       first->prev = new_one; 
       first = new_one; 
      } 
      // position > 0 
      else { 
       dllist_entry* elem = getElementRaw(position); 
       new_one->next = elem; 
       new_one->prev = elem->prev; 
       elem->prev->next = new_one; 
       elem->prev = new_one; 
      } 
     } 
     else if(position == getCount()) { 
       new_one->next = NULL; 
      new_one->prev = last; 
      last->next = new_one; 
      last = new_one; 
     } 
     else { 
      throw OutOfBoundsException(); 
     } 
    } 
    else { 
     new_one->next = NULL; 
     new_one->prev = NULL; 
     first = new_one; 
     last = new_one; 
    } 

    length++; 
}  

template <typename T> 
T List<T>::pop(unsigned int position) { 
    T value = getElement(position); 
    remove(position); 
    return value; 
} 

template <typename T> 
void List<T>::remove(unsigned int position) { 
    dllist_entry* elem = getElementRaw(position); 


    if(getCount() == 1) { // entspricht elem == first && elem == last 
     first = NULL; 
     last = NULL; 
    } 
    else if(elem == first) { 
     elem->next->prev = NULL; 
     first = elem->next; 
    } 
    else if(elem == last) { 
     elem->prev->next = NULL; 
     last = elem->prev; 
    } 
    // Element liegt zwischen Anfang und Ende 
    // (Wäre das nicht so, hätte getElementRaw() bereits protestiert.) 
    else { 
     elem->prev->next = elem->next; 
     elem->next->prev = elem->prev; 
    } 

    delete elem; 
    length--; 
} 

template <typename T> 
T List<T>::getElement(unsigned int position) const { 
    return getElementRaw(position)->value; 
} 

template <typename T> 
typename List<T>::dllist_entry* List<T>::getElementRaw(unsigned int position) const { 
    // schließt den Fall getCount() == 0 mit ein 
    if(position < getCount()) { 
     dllist_entry* iter; 

     // aus Performance-Gründen mit der Suche entweder von vorne oder 
     // von hinten beginnen 
     if(position <= (getCount() - 1)/2) { 
      iter = first; 

      for(unsigned int i=0; i < position; i++) { 
       iter = iter->next; 
      } 
     } 
     else { 
      iter = last; 

      for(unsigned int i = getCount() - 1 ; i > position; i--) { 
       iter = iter->prev; 
      } 
     } 

     return iter; 
    } 
    else { 
     throw OutOfBoundsException(); 
    } 
} 





IntList::IntList() : List<int>() { } 
IntList::IntList(const IntList& l) : List<int>(l) { } 
IntList::~IntList() { } 

IntList& IntList::operator=(const IntList& l) { 
    List<int>::operator=(l); 
    return *this; 
} 


int IntList::min() const { 
    // erstes Element separat holen, damit OutOfBoundsException geworfen werden 
    // kann, wenn Liste leer ist 
    int min = getElement(0); 

    for(unsigned int i=1; i < getCount(); i++) { 
     int value = getElement(i); 
     if(value < min) { 
      min = value; 
     } 
    } 

    return min; 
} 

int IntList::max() const { 
    // erstes Element separat holen, damit OutOfBoundsException geworfen werden 
    // kann, wenn Liste leer ist 
    int max = getElement(0); 

    for(unsigned int i=1; i < getCount(); i++) { 
     int value = getElement(i); 
     if(value > max) { 
      max = value; 
     } 
    } 

    return max; 
} 

float IntList::average() const { 
    if(getCount() > 0) { 
     int sum = 0; 

     for(unsigned int i=0; i < getCount(); i++) { 
      sum += getElement(i); 
     } 

     return (float) sum/getCount(); 
    } 
    else { 
     return 0; 
    } 
} 

大きなソースのため申し訳ありませんが、私は私だけ抽出を掲載場合、私は誤って何かを残すことができる恐れていました。

レコードの場合:list.hpp/list.cppのdestructor〜IntList()を明示的に宣言または定義する前に、List ::〜List()で同様のエラーメッセージが表示されました。実際には、IntListオブジェクトを破棄するときに、親クラスListのデストラクタが呼び出されているので、宣言する必要はありません。また、デストラクタを "〜IntList(){}"というヘッダファイルlist.hppに直接定義しても、トリックは行いませんでした.dtor定義をlist.cppに移動するまで、エラーメッセージは消えませんでした。

サイドノート:それは私が1つの大きなファイルにまだそれを持っていたときにすべてが素晴らしくコンパイルされました。

このバグを狩ってくれてありがとう! :)

+0

〜IntList関数があると主張しました。あなたがそのような主張をするならば、あなたはそれをバックアップしなければなりません。それ以外の場合は、仮想関数テーブルのインスタンシエーションまたはテンプレートのインスタンス化を解除できます。 –

答えて

4

テンプレートの定義は、宣言が提供されているファイルと同じでなければなりません。彼らは分離することはできません。

だから、list.hpplist.cppのすべてdefinitonsを移動したり、list.hpp

#ifndef __LIST_HPP__ 
#define __LIST_HPP__ 

template <typename T> 
class List { 

//... 

}; 

class IntList : public List<int> { 

//... 

}; 

#include "list.cpp" //<----------------- do this 

#endif 

でこれを行うと、list.cppファイルから行を削除し#include list.hppのいずれか。

#include <iostream> 
//#include "list.hpp" //remove this 
using namespace std; //<----- don't do this either - see the note below! 


template <typename T> 
List<T>::List() { 
    this->first = NULL; 
    this->last = NULL; 
    this->length = 0; 
} 
//.... 

補足として、using namespace stdを使用しました。次のような完全修飾名を使用してください。

std::vector<int> v; 
std::sort(...); 
//etc 
+1

ありがとう、それは今、動作します! :) – balu

+0

ところで:なぜいくつかの方法だけでエラーが発生したのですか? – balu

1

テンプレートコードはヘッダーファイルである必要があります。

テンプレートは、コードで使用するときに「オンデマンドで」生成されるコンパイラ生成コードです。

ヘッダーファイルにない場合は、適切なコードを見つけて特定の種類のコードを生成できません。

あなたが行ったことは、List<int>から継承して、独自の実装でメンバー関数を隠したクラスを作成したことです。

print()を実装しておらず、テンプレートのソースがファイルに含まれていないため、List<int>.print()のコードを生成できませんでした。

編集:

のみprint()は、エラーが発生した理由だけで明確にする:

あなたは3つの機能使用しているあなたのmain機能では:今getCount()insert()print()

を、見てみましょうList<int>の実装:

コピーコンストラクタでは、 List(const List& l)を呼び出す....

IntList::IntList(const IntList& l) : List<int>(l) { } 

コンストラクタはinsert()getCount()getElement()を呼び出す:

for(unsigned int i=0; i < l.getCount(); i++) { 
    insert(l.getElement(i)); 
} 

クラスIntListがコンパイルされるときに、これらすべての機能が作成されます。
IntListの実装では、テンプレート実装が「作成」され、それらの関数が作成されます。

一方、main関数ではprint<int>()が初めて呼び出されますが、この関数はテンプレート実装を認識しません。

+0

それをさらに説明してくれてありがとう。しかし、insert()と他のすべてのメソッドはうまくいきましたか? – balu

+0

@ codeethiefそれはあなたがそれらを使ったからです。 List から継承したクラスは 'getElement()'、 'getCount()'、 'List ()'コンストラクタを呼び出し、そのコンストラクタは 'insert()'を呼び出します。クラス。 –