2012-02-08 108 views
4

我想在C++中有独立的汇编编写一个程序,我写了这个:模板和独立编译

的main.cpp

#include <iostream> 
#include "Stack.h" 
using namespace std; 
int main(int argc,char* argv[]) 
{ 
    Stack<int> st; 
    st.push(1); 
    return 0; 
} 

Stack.h

#ifndef _STACK_H 
#define _STACK_H 
template<typename T> 
class Stack 
{ 
private: 
struct Node 
{ 
    Node* _prev; 
    T _data; 
    Node* _next; 
}; 
int _size; 
Node* _pos; 

public: 
    Stack(); 
    T pop(); 
    void push(T const &el); 
    int getSize() const; 
}; 
#endif 

Stack.hpp

#include "Stack.h" 
#include <malloc.h> 
template <typename T> 
Stack<T>::Stack() 
{ 
    _size = 0; 
    _pos = (Node*)malloc(sizeof(Node)); 
    _pos->_prev = NULL; 
    _pos->_next = NULL; 
} 
template <typename T> 
T Stack<T>::pop() 
{ 
    if (_size == 0) 
     return NULL; 
    T tmp = _pos->_data; 
    if (_pos->_prev == NULL) 
     free(_pos); 
    else 
    { 
     _pos->_prev->_next = _pos->_next; 
     if (_pos->_next != NULL) 
     { 
    _pos->_next->_prev = _pos->_prev; 
     } 
     free(_pos); 
    } 
    _size--; 
    return tmp; 
} 
template <typename T> 
void Stack<T>::push(T const &el) 
{ 
    Node* n = (Node*)malloc(sizeof(Node)); 
    _pos->_next = n; 
    n->_prev = _pos; 
    n->_data = *el; 
    _pos = n; 
    _size ++; 
} 
template<typename T> 
int Stack<T>::getSize() const {return _size;}; 

我编译d与G ++程序,我得到这个错误:

ccyDhLTv.o:main.cpp:(.text+0x16): undefin 
ed reference to `Stack<int>::Stack()' 
ccyDhLTv.o:main.cpp:(.text+0x32): undefin 
ed reference to `Stack<int>::push(int const&)' 
collect2: ld returned 1 exit status 

我知道这个问题是因为我使用的模板,但我不知道如何解决它。

操作系统 - 视窗 编译行 - g++ main.cpp Stack.h Stack.hpp -o main.exe

+0

可能的重复[为什么模板类的实现和声明应该在同一个头文件中?](http://stackoverflow.com/questions/3749099/why-should-the-implementation-和模板类的声明) – 2012-02-08 17:04:27

回答

4

模板类需要有头文件中的方法定义。

将您在.cpp文件中的代码移动到标题中,或者创建一个名为.impl.imp的文件,将代码移动到标题中并将其包含在标题中。

编译器需要知道方法定义以生成所有专业化的代码。

在你问之前,不,没有办法将实现保持在头之外。

+2

所以你想说当我使用模板时我不能使用单独的编译? – 2012-02-08 10:12:39

+0

@JordanBorisov。 – 2012-02-08 10:13:37

+0

@JordanBorisov见http://www.parashift.com/c++-faq-lite/templates.html - 第12条。 – 2012-02-08 10:17:28

0

我会说它会更实际一些,首先了解单独编译如何工作于正常(未被修改的)文件,然后理解g ++编译器如何为模板执行编译。

首先,在普通文件中,当只包含声明的头文件包含在主文件中时,预处理程序会从声明头替换声明并将其放到主文件中。然后,在预处理阶段结束后,编译器逐个编译包含在.cpp文件中的纯C++源代码并将其转换为目标文件。此时编译器不介意缺少的定义(函数/类),而且目标文件可以引用未定义的符号。因此编译器只要编译完好,就可以编译源代码。

然后,在链接阶段,编译器将多个文件链接在一起,并且在此阶段链接器将在丢失/重复定义时产生错误。如果函数定义在其他文件中正确显示,则链接器继续执行,并且从主文件调用的函数成功链接到该定义并可以使用。

对于模板,事情的工作方式不同。这将是说明性的考虑一个例子,所以我挑选简单:

考虑模板阵列类的头文件中:

array.h

#ifndef _TEMPLATE_ARRAY_H_ 
#define _TEMPLATE_ARRAY_H_ 
template <class T> 
class Array 
{ 
    private: 
     T *m_list; 
     int m_length; 

    public: 
     Array() //default constructor 
     { 
      m_list = nullptr; 
      m_length = 0; 
     } 


     Array(int length) 
     { 
      m_list = new T[length]; 
      m_length = length; 
     } 

     ~Arrary() 
     { 
      delete[] m_list; 
      m_list = nullptr; 
     } 

     //undefined functions 
     int getLength(); 
     T getElement(const int pos); 
}; 

和相应的数组。CPP文件:

include "array.h" 

template <class T> 
array<T>::getLength() 
{ return m_length; } 

template <class T> 
T Array<T>::getElement(const int pos) 
{ return m_list[pos]; } 

现在考虑在其中创建模板对象阵列的两个实例,一个用于int和另一个用于双主文件。

的main.cpp

#include "array.h" 
#include <iostream> 


int main() 
{ 
    Array<int> int_array; 
    Array<double> double_array; 

    std::cout << int_array.getLength() <<"\n"; 
    std::cout << double_array.getLength() << "\n"; 
} 

当此片的代码被编译,则预处理器第一副本从所述头文件到主文件照常模板声明。由于在主文件Array> < int>和Array < double>对象被实例化时,编译器实例化Array类的两个不同定义,每个对象用于double和int,然后实例化main.cpp文件中的Array对象。

请注意,直到此时,main.cpp文件中仍缺少double> :: getLength()的int> :: getLength()和Array <的函数定义,但由于源代码格式良好,编译器毫无麻烦地编译main.cpp文件。总而言之,到目前为止,没有区别黑白模板对象/函数编译和非模板函数编译。

与此同时编译包含数组< T> :: getLength()和数组<的模板函数定义的array.cpp的代码文件,但此时编译器将拥有忘记了main.cpp中需要阵列< int>的::的getLength()和阵列<双> ::的getLength()和将愉快地编译代码array.cpp而不产生由所需的函数定义的int和双版本的任一实例main.cpp文件。 (请记住,编译器分别编译每个文件!)

这是在链接阶段,由于缺少函数定义以及主文件所需的模板函数定义的双重版本,可怕的模板错误开始弹出。在非模板声明和定义的情况下,程序员确保在文件中定义查找函数,该文件可以与调用该函数的文件链接在一起。但是在模板的情况下,在编译阶段后执行的链接程序不能执行编译器应该做的任务,即生成代码,在这种情况下,模板函数的int和double类型为

我们可以很容易得出这样的结论:围绕模板分离编译的整个过程都是由于链接(即),如果所有的代码都被正确写入,在头部声明的类和函数并在另一个单独的文件中定义)。解决此获得的方法是:

  1. 定义在标题的类和函数文件本身,而不是单独的文件中,这样的头文件中包含的主要文件时的内容,包括模板定义,从而导致相应的必须由编译器定义的函数实例。

  2. 实例化,你知道你需要在模板中定义写在不同的文件类型定义。这将直接链接到主文件中的函数调用。

  3. 另一种方式来解决这个问题是命名的.cpp文件,其中定义写入.INL *文件(从e.g上面绘制,chagne array.cpp到array.inl); inl意味着内联并包含。inl文件从头文件的底部。这与在头文件中定义所有函数的结果相同,但有助于使代码更清晰。

  4. 还有另一种方法,即#include .cpp文件与主文件中的模板化定义,我个人不喜欢因为#include的非标准用法。