2009-06-16 23 views
0

我刚刚创建这个新类:C++ - 未定义的最近创建的类的引用!

//------------------------------------------------------------------------------ 
#ifndef MULTITHREADEDVECTOR_H 
#define MULTITHREADEDVECTOR_H 
//------------------------------------------------------------------------------ 
#include <vector> 
#include <GL/GLFW.h> 
//------------------------------------------------------------------------------ 
template<class T> 
class MultithreadedVector { 

    public: 

     MultithreadedVector(); 

     void push_back(T data); 

     void erase(typename std::vector<T>::iterator it); 

     std::vector<T> get_container(); 
    private: 

     std::vector<T> container_; 
     GLFWmutex th_mutex_; 


}; 
//------------------------------------------------------------------------------ 
#endif // MULTITHREADEDVECTOR_H_INCLUDED 
//------------------------------------------------------------------------------ 

类的定义:

//------------------------------------------------------------------------------ 
#include "MultithreadedVector.h" 
//------------------------------------------------------------------------------ 
using namespace std; 
//------------------------------------------------------------------------------ 
template<class T> 
MultithreadedVector<T>::MultithreadedVector() { 

    th_mutex_ = glfwCreateMutex(); 
} 

template<class T> 
void MultithreadedVector<T>::push_back(T data) { 

    glfwLockMutex(th_mutex_); 
    container_.push_back(data); 
    glfwUnlockMutex(th_mutex_); 

} 

template<class T> 
void MultithreadedVector<T>::erase(typename vector<T>::iterator it) { 

    glfwLockMutex(th_mutex_); 
    container_.erase(it); 
    glfwUnlockMutex(th_mutex_); 
} 

template<class T> 
vector<T> MultithreadedVector<T>::get_container() { 


    return container_; 

} 

现在的问题是,当我尝试使用它在我的代码作为另一个类的静态成员:

// VehicleManager.h 
#ifndef MULTITHREADEDVECTOR_H 
#define MULTITHREADEDVECTOR_H 

#include "MultithreadedVector.h" 
#include "Vehicle.h" 
class Foo { 

    public: 
    // stuffs 
    private: 
    static MultithreadedVector<Vehicle> vehicles_; 
    ... 
} 

#endif 

然后里面:VehicleManager.cpp

#include "VehicleManager.h" 

MultithreadedVector<Vehicle> VehicleManager::vehicles_; 

void VehicleManager::Method() { 

    Vehicle vehicle; 
    VehicleManager::vehicles_.push_back(vehicle); 

} 

不过,这并不编译:(,我得到这个错误味精每次:

C:\***\VehicleManager.cpp|188|undefined reference to `MultithreadedVector<Vehicle>::push_back(Vehicle)'| 

我真的不明白为什么,尤其是我在VehicleManager全球范围定义的类的静态成员的.cpp。

PS:我正在使用Code :: Blocks。

谢谢!

+1

术语警察:MultithreadedVector中的那件事。h *是类(模板)的定义。您称为“类的定义”的cpp文件不包含类的定义,它包含仅在类定义中声明(未定义)的类成员的定义。类模板的声明(未定义)如下所示:“template class MultithreadedVector;”。也称为“前向声明”,以区别于定义,因为每个定义都是一个声明。 – 2009-06-16 14:29:06

+0

你是对的..感谢您的澄清!最快打字员 – 2009-06-16 15:24:18

回答

5

这就是盲目地将类模板实现移动到cpp文件时会发生的情况。

为了做到这一点,您必须准确知道T的类型,您将实例化类模板并在cpp文件中指定它们。

在这种情况下,把这个cpp文件:

template class MultithreadedVector<Vehicle>; 

注意cpp文件必须知道Vehicle然后。

2

模板实现的完整代码必须在编译器看到使用模板的位置时可见,以便它可以实例化模板并将其编译为可稍后链接的对象代码。

在声明模板的同一头文件中定义模板代码。

4

大多数C++编译器不允许你分开模板声明和模板定义。您需要将模板类的完整定义放在一个.h文件中,而不是将它们分割为.h和.cpp文件。

+0

再次获胜。我会删除我的重复答案 – Glen 2009-06-16 13:14:38

+0

哦,是的,没错,完全忘了!谢谢 ! – 2009-06-16 13:20:16

+0

几年前,我会同意你的看法。然而,在这一点上,最常用的C++编译器(Borland,MSVC,IBM,gcc)都支持通过`extern template`语法和显式实例化来分隔模板声明和模板定义。这就是说,在C++ 0x发生之前,这将不是标准的。 – 2009-06-16 14:46:02

1

你似乎有一个副本&粘贴错误VehicleManager.h:

#ifndef MULTITHREADEDVECTOR_H 
#define MULTITHREADEDVECTOR_H 

这禁止的头文件,在类Foo定义列入。

1

在大多数情况下,您无法在cpp文件中编写模板实现。将模板实现移动到头文件,以便编译器可以正确实例化它。

1

除了其他答案:

你必须警惕get_container()为好。此方法基本上复制container_元素,因此必须保护。

template<class T> 
vector<T> MultithreadedVector<T>::get_container() 
{ 
    return container_; 
} 
1

一种方式中的模板是一种先进的专用类型的宏。你不能单独编译一个模板定义,就像你试图做的那样。

对于大多数人来说,这意味着他们甚至不打扰将teplate定义与其声明分开。我有一个同事takes this a step further,并拒绝分开非模板的定义和声明。

如果你想从声明中放置模板的定义,你确实有两个选项。

第一个选项是使用C++“导出”关键字。这个看似简单的解决方案的问题是nobody supports it。编译器编写者实施起来太困难了。

第二个选项是使用third type of file(通常用“.ipp”标记)作为声明。这里的诀窍是它仍然是一个文件,必须是“#included”,但作为一个单独的文件,你只需要将它包含在“.cpp”文件中。我发现我的链接器不喜欢它,如果我在一个程序中包含多个“.cpp”文件中的“.ipp”,所以你必须选择一个作为包装器。

2

我认为有两种情况可以如何使用模板

  1. 提供符合各类
  2. 任意一组提供通用代码为一组固定的类型

对于通用代码第二个,你不需要把模板放到标题中:既然你事先知道,确切地说,你的模板使用了什么类型。您可以很好地定义类模板的成员函数,或者在单独的翻译单元中定义函数模板,而无需链接器错误。有些人一直教导别人这是不可能的,并通过这个来混淆这个问题。

但我认为这取决于这两种情况是否要将声明和定义分隔到不同的翻译单元中。

  • 如果你想使用情况1,你总是希望有在标题中定义(是否包括特殊命名的文件,如.ipp.tcc或其他)。我只知道一个支持从声明中分离定义的C++前端,即使在这种情况下,即EDG(edison设计组)前端,由intel和comeau编译器使用,实现export。该关键字被某些人认为是错误的,大多数编译器都没有实现它。

  • 如果你想使用第二种情况,将定义放入头文件是没有用的,因为没有理由这么做:你只需要显式地实例化所需的函数和类,类型。在第二种情况下,您可以使用该模板来免除维护可能的冗余代码的负担,并且在某些情况下可以选择打开某些类型的特殊行为。

你的情况是清楚的情况下1的情况,所以你必须把你定义成头。