使用模板将堆栈实现为链接列表时出现 "symbols not found" 错误

Getting "symbols not found" errors implementing a Stack as Linked List using templates

我正在尝试使用链表创建堆栈。我已经使用整数让它工作了,但现在我想为它实现模板。编译前没有检测到错误,但在 运行 之后我得到这些错误:

*未定义的体系结构符号x86_84: "Stack::printStack()",引用自: _main main.o

"Stack::pop()",引用自: _main main.o

"Stack::push(int)",引用自: _main main.o

"Stack::~Stack()",引用自: _main main.o

ld:未找到体系结构的符号 x86_64 clang:错误:链接器命令失败,退出代码为 1(使用 -v 查看调用) *

我认为我的麻烦来自于必须定义两个模板,一个在 Stack.h 文件中,一个在我的 LinkedList.h 文件中。在我的 Stack.h 文件中,我需要调用 Node,如果不包含模板参数我就无法调用 Node,如果我不包含另一个模板定义,Stack.h 将无法识别模板参数。现在有些情况下我只使用 'T' 而不是 Node,但我已经尝试了两种方法。我觉得我已经尝试了一切,但我无法弄清楚我的错误在哪里。在这一点上,我真的只是为了概念证明,这就是为什么我没有为 Stack 添加复制构造函数或其他函数的原因。我的代码如下。

LinkedList.h

#ifndef Linked_List__Stack_LinkedList_h
#define Linked_List__Stack_LinkedList_h

template<class T>
struct Node {
public:
    Node(Node *n, T data) : next(n), data(data) {}
    Node *getNext() const { return next; }
    T value() const { return data; }
private:
    Node* next;
    T data;
};

#endif

Stack.h

    #ifndef __Linked_List__Stack__Stack__
    #define __Linked_List__Stack__Stack__

    #include "LinkedList.h"
    #include <stdio.h>
    #include <iostream>




    using namespace std;

    template <class T>
    class Stack {

    public:
        Stack() : head(NULL), tail(NULL) {};
        ~Stack();
        void push(T data);
        T pop();
        void printStack();

        private:

        Node<T> *head;
        Node<T> *tail;

    };


#endif /* defined(__Linked_List__Stack__Stack__) */

Stack.cpp

    #include "Stack.h"

template <class T>
Stack<T>::~Stack() {
    while (head) {
        Node<T>* temp = head->getNext();
        delete head;
        head = temp;
    }
    head = tail = NULL;
}

template <class T>
void Stack<T>::push(T data) {

    Node<T>* node = new Node<T>(head, data);
    head = node;
    if (head->getNext() == NULL )
        tail = head;

}

template <class T>
T Stack<T>::pop() {

    Node<T> *top = head;
    T data;

    if(head == NULL)
        throw; //StackError(E_EMPTY);

    data = head->value();
    head = head->getNext();
    delete top;
    return data;
}

template <class T>
void Stack<T>::printStack() {
    Node<T> *current = head;
    while (current != tail) {

        cout << current->value() << ", ";

        current = current->getNext();
    }

    cout << current->value() << endl;
}

main.cpp

#include "Stack.h"
#include <iostream>
#include <stdexcept>

int main(int argc, const char * argv[]) {


    Stack<int> *myIntStack = new Stack<int>();


        myIntStack->push(10);
        myIntStack->printStack();

        myIntStack->pop();
        myIntStack->printStack();

    delete myIntStack;

    return 0;
}

几乎没有流行的编译器允许您将模板定义作为单独的编译单元放在 cpp 文件中(因此出现链接器错误)。

简而言之,只需将您的 Stack.cpp 的内容移至 Stack.h(并忘记您的 Stack.cpp),然后一切都应该有效。

我喜欢使用的另一种方式(好吧...十多年前,当我还在工作中编写 C++ 时)是 "include" header 中的模板源,例如:

Foo.h

#ifndef FOO_H__
#define FOO_H__

TEMPLATE_DECL template <class T> Foo {
....
};

#ifndef SUPPORT_TEMPLATE_EXPORT
#include "Foo.tmpl"  // include it if you CANNOT compile it as separate compilation unit
#endif

#endif  //ifndef FOO_H__

Foo.tmpl(我使用另一个扩展名来区别于.cpp)

#ifdef SUPPORT_TEMPLATE_EXPORT
#include "Foo.h"   // only include if you can compile it as separate compilation unit
#endif

// template definition
TEMPLATE_DEF Foo<T>::bar() {....}

当然,您需要注意预处理器和编译器设置,但这应该是微不足道的。而且,模板导出从 C++0x 开始被弃用,如果您不关心在旧编译器(例如 Comenu)中使用此功能,则可以进一步简化此方法


一些东西 off-topic:避免在你的 header 中使用名称空间(或实际上任何 using 子句)。会造成命名空间污染

正如 Adrian 指出的那样,编译器需要在实例化时拥有所有可用的模板实现,我同意他的建议,只需将实现移至 Stack.h.

但是,如果您出于自己的目的想要将接口和实现分开,您可以 #include "Stack.cpp"Stack.h 的底部(并从 [=14] 中删除 #include "Stack.h" =]).