简体   繁体   English

C ++:链接器错误:未定义的引用仅对在单独的文件中定义的一个特定的类成员

[英]C++: Linker error: undefined reference only to one specific class member defined in a separate file

I'm receiving the infamous "undefined reference" error when trying to compile & link several files and would be glad if you could help. 尝试编译和链接多个文件时,我收到臭名昭著的“未定义引用”错误,如果您能提供帮助,我将非常高兴。

The exact error message: 确切的错误信息:

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

main.cpp: 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;
}

The funny thing is: neither the member function insert() nor min(), max() or average() cause any problems. 有趣的是:成员函数insert()或min(),max()或average()都不会引起任何问题。 It's just print(). 这只是print()。

[EDIT]: It's not just print(), but also remove(). [编辑]:它不仅是print(),而且是remove()。

list.hpp: 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: 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;
    }
}

Sorry for the large source but I was afraid I could accidentally leave something out if I only posted extracts. 抱歉,请提供大量信息,但我担心如果仅发布摘录,可能会意外遗漏某些内容。

For the record: I received a similar error message – this time with List::~List() – before I explicitely declared / defined the destructor ~IntList() in list.hpp / list.cpp. 作为记录:在我明确声明/定义list.hpp / list.cpp中的析构函数〜IntList()之前,我收到了类似的错误消息–这次是List ::〜List()。 I actually expected I shouldn't even need to declare it since the destructor of the parent class List is called anyway when destroying an IntList object? 我实际上希望我什至不需要声明它,因为销毁IntList对象时仍会调用父类List的析构函数? Also, even defining the destructor directly in the header file list.hpp as "~IntList() { }" didn't do the trick – the error message wouldn't disappear until I moved the dtor definition to list.cpp. 另外,即使直接在头文件list.hpp中将析构函数定义为“〜IntList(){}”也无法解决问题–在将dtor定义移至list.cpp之前,错误消息不会消失。

On a side note: The whole thing compiled wonderfully when I had it still in one big file. 附带说明:当我将其保存在一个大文件中时,整个过程进行了出色的编译。

Thanks for taking the time hunting this bug down! 感谢您抽出宝贵的时间来寻找该bug! :) :)

The definition of template should be in same file in which declaration is provided. 模板的定义应该在提供声明的同一文件中。 They cannot be separated. 它们不能分开。

So either you move all the definitons of list.cpp to list.hpp , Or do this in list.hpp 因此,您可以将list.cpp所有定义移动到list.hpp ,或者在list.hpp执行此list.hpp

#ifndef __LIST_HPP__
#define __LIST_HPP__

template <typename T>
class List {

//...

};

class IntList : public List<int> {

//...

};

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

#endif

And remove the line #include list.hpp from list.cpp file. 并从list.cpp文件中删除行#include list.hpp It is making it circular: 使其成为圆形:

#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;
}
//....

As a side note, done use using namespace std . 作为附带说明,请using namespace std完成使用。 Use fully-qualified name, such as: 使用标准名称,例如:

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

Templated code needs to be in header files. 模板化代码必须位于头文件中。

Templates are compiler generated code that gets generated "on demand" when you use it in your code. 模板是编译器生成的代码,当您在代码中使用模板时,这些代码将“按需”生成。

If it's not in the header file, it can't find the proper code and generate it for the specific type. 如果它不在头文件中,则找不到合适的代码并为特定类型生成它。

What you've done is created a class that inherits from List<int> and then hidden a the member functions with your own implementation. 完成的工作是创建一个从List<int>继承的类,然后使用自己的实现隐藏成员函数。

But you did not implement print() , and because the source of the template isn't included in the file, the code for List<int>.print() couldn't be generated. 但是您没有实现print() ,并且由于模板的源未包含在文件中,因此无法生成List<int>.print()的代码。

Edit: 编辑:

Just to clarify why only print() raised an error: 只是为了阐明为什么只有print()引发错误:

In your main function you're using 3 functions: getCount() insert() and print() main函数中,您使用3个函数: getCount() insert()print()

Now, lets look at your implementation of List<int> : 现在,让我们看一下List<int>

In your copy constructor you call List(const List& l) .... 在副本构造函数中,调用List(const List& l) ...。

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

That constructor calls insert() , getCount() and getElement() : 该构造函数调用insert()getCount()getElement()

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

So all these functions are created when the class IntList is compiled. 因此,所有这些函数都是在编译类IntList时创建的。
The implementation of IntList "sees" the template implementations so those functions are created. IntList的实现“看到”模板的实现,因此创建了这些功能。

On the other hand, print<int>() is only called for the first time in the main function which doesn't "see" the template implementation. 另一方面,仅在不“看到”模板实现的main函数中第一次调用print<int>()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM