模板的声明和实现是可以分开的,但是有很多缺点,所以不建议分开,建议都写在 hpp 文件里面
1. 模板中通常声明和实现是放在同一个 hpp 里面的。
否则在链接的时候会报错。因为在编译的时候,模板会根据实现的具体类型进行展开,形成具体类型的函数或类等。当链接的时候,就可以链接到这些具体的实例上面了。 如果没有实现,那么就不会生成对应的具体类型,链接的时候,自然找不到对应的函数,类,变量等等。
2. 如果一定要声明和实现分开怎么做?
一定要分开有好几种方法能够实现,简单可以分为两类,一种是include
相应的实现所在的文件,一种是显式实例化。
3. include
实现的方法
主要是把声明和定义的文件都通过 include 放到实现的文件中,这样相当于声明和实现没有分开。 实现的具体方法
// TestTemp.h
#ifndef _TESTTEMP_H_
#define _TESTTEMP_H_
template<class T>
class TestTemp
{
public:
TestTemp();
void SetValue( T obj_i );
T Getalue();
private:
T m_Obj;
};
#endif
// TestTemp.cpp
#include "TestTemp.h"
template <class T>
TestTemp<T>::TestTemp()
{
}
template <class T>
void TestTemp<T>::SetValue( T obj_i )
{
}
template <class T>
T TestTemp<T>::Getalue()
{
return m_Obj;
}
// Client.cpp
#include "TestTemp.h"
#include "TestTemp.cpp"
:
TestTemp<int> TempObj;
TempObj.SetValue( 2 );
int nValue = TempObj.Getalue();
:
// TestTemp.h
#ifndef _TESTTEMP_H_
#define _TESTTEMP_H_
template<class T>
class TestTemp
{
public:
TestTemp();
void SetValue( T obj_i );
T Getalue();
private:
T m_Obj;
};
#include "TestTemp.cpp"
#endif
// TestTemp.cpp
#include "TestTemp.h"
template <class T>
TestTemp<T>::TestTemp()
{
}
template <class T>
void TestTemp<T>::SetValue( T obj_i )
{
}
template <class T>
T TestTemp<T>::Getalue()
{
return m_Obj;
}
// Client.cpp
#include "TestTemp.h"
:
TestTemp<int> TempObj;
TempObj.SetValue( 2 );
int nValue = TempObj.Getalue();
:
参考: https://www.codeproject.com/Articles/48575/How-to-define-a-template-class-in-a-h-file-and-imp
4. 显式实例化
显示实例化主要是在定义的文件中提前说好有哪几种类型可以给模板使用。这样在实现的时候,只能对模板使用这几种类型。
// TestTemp.h
#ifndef _TESTTEMP_H_
#define _TESTTEMP_H_
template<class T>
class TestTemp
{
public:
TestTemp();
void SetValue( T obj_i );
T Getalue();
private:
T m_Obj;
};
#endif
// TestTemp.cpp
#include "TestTemp.h"
template <class T>
TestTemp<T>::TestTemp()
{
}
template <class T>
void TestTemp<T>::SetValue( T obj_i )
{
}
template <class T>
T TestTemp<T>::Getalue()
{
return m_Obj;
}
// No need to call this TemporaryFunction() function,
// it's just to avoid link error.
void TemporaryFunction ()
{
TestTemp<int> TempObj;
}
// Client.cpp
#include "TestTemp.h"
:
TestTemp<int> TempObj;
TempObj.SetValue( 2 );
int nValue = TempObj.Getalue();
:
// template.h
#ifndef TEMPLATE_H
#define TEMPLATE_H
#include <iostream>
template <typename T>
class myTemplate
{
private:
T data;
public:
myTemplate();
myTemplate(T t);
T getData() const;
void displayData() const;
static int someValue;
};
#endif
// template.cpp
#include "template.h"
#include <iostream>
//template functions
template<typename T>
myTemplate<T>::myTemplate()
:data()
{
}
template<typename T>
myTemplate<T>::myTemplate(T t)
:data(t)
{
}
template <typename T>
T myTemplate<T>::getData() const
{
return data;
}
template <typename T>
void myTemplate<T>::displayData() const
{
std::cout << data <<std::endl;
}
template<typename T>
int myTemplate<T>::someValue = 100;
//The explicit instantiation part
template class myTemplate<int>;
template class myTemplate<float>;
// main.cpp
#include <iostream>
#include "template.h"
using namespace std;
int main()
{
myTemplate<int> myTi(5);
myTi.displayData();
myTemplate<float> myTf(3.5f);
myTf.displayData();
return 0;
}
参考: http://www.cplusplus.com/forum/articles/14272/ https://stackoverflow.com/questions/1724036/splitting-templated-c-classes-into-hpp-cpp-files-is-it-possible https://www.zhihu.com/question/20630104
5. 分开的缺点
include
的方法直接包含源文件比较恶心。 显式实例化只能在预先选好的类型中选择,限制了可用的类型。所以一般声明和实现放一起,这样就变成类似开源的代码了。
6. 模板的缺点
当使用模板的时候,如果调用模板的类也没有指定相应的类型,那么这个调用的类也必须是模板类,这样一直向上感染,直到碰到一层对这个模板进行了实例,指定相应的类型,才会停止。这种向上感染,很麻烦。