最近使用了c++模板,觉得非常强大,只是写起来需要掌握一点技巧。大部分模板都是直接把定义写在.h头文件,并且有些人还说这样做的原因是模板不支持分编译,可是以前的编译器对模板的支持不够好吧,但是现在完全可以。
环境:
win7 32旗舰版、VS2010 sp1
1、普通函数模板
define.h文件
1 template<typename T> 2 T GetString(int value);
define.cpp文件
1 #include "define.h" 2 3 template<typename T> 4 T GetString<T>(int value) 5 { 6 return T(value); 7 } 8 9 //偏特化 10 template<> 11 string GetString<string>(int value) 12 { 13 char psz[32] = {0}; 14 itoa(value, psz, 10); 15 return string(psz); 16 } 17 18 //偏特化 19 template<> 20 wstring GetString<wstring>(int value) 21 { 22 wchar_t psz[32] = {0}; 23 _itow(value, psz, 10); 24 return wstring(psz); 25 }
如果我们直接包含define.h后,使用GetString<int>(100)函数调用,是使用不了的,会产生链接错误,为什么呢?我们上面定了函数模板的通用 实现,但是并没有实例化为一个真正的函数,既然不是函数,编译器当然不会生成代码,所以会出现链接错误;要解决这个问题的最主要就是实例化函 数模板使之成为一个真正的函数,第一种办法是:使用上面偏特化的方法,例如string和wstring类型的偏特化,因为上面已经写了,所以不多说了; 下面说第二种办法:
1 #define TEMPLATE_GET_STRING(T) template T GetString<T>(int value); 2 TEMPLATE_GET_STRING(int)//函数模板实例化 3 // 如果要在dll中导出此函数,我们可以这样 4 template __declspec(dllexport) int GetString<int>(int value);
2、类模板
define.h文件
1 template<typename T> 2 class A 3 { 4 public: 5 A() 6 { 7 InitValue(); 8 } 9 10 void InitValue(); 11 12 int m_nValue; 13 };
define.cpp文件
1 template<typename T> 2 A<T>::A() 3 { 4 InitValue(); 5 } 6 7 template<> 8 void A<string>::InitValue() 9 { 10 m_nValue = 100; 11 } 12 13 template<> 14 void A<int>::InitValue() 15 { 16 m_nValue = 200; 17 } 18 19 // 类模板实例化 20 template 21 A<int>;
3、成员函数模板
define.h文件
1 class B 2 { 3 public: 4 template<typename T> 5 T Get(int index); 6 };
define.cpp文件
1 //成员函数模板偏特化 2 template<> 3 double B::Get<double>(int index) 4 { 5 return 2.1; 6 } 7 8 //成员函数模板偏特化 9 template<> 10 int B::Get<int><int>(int index) 11 { 12 return 1; 13 } 14 </int> 15 //如果类B是一个数据库封装类,我们就可以利用这样的调用方式来获取数据库一条记录中的字段 16 17 B b; 18 b.Get<int>(1);//获取当前记录中第一个字段的值,并转化为int类型 19 b.Get<double>(2)//获取当前记录中第二个字段的值,并转化为double类型
4、将类型与一些数据进行绑定
有时候我们需要将类型与一些数据进行绑定,例如我们一般通过单例,将字符串与一个函数关联(注册),之后通过字符串创建对象
1 class A { 2 public: 3 static A* Create() { return new A(); } 4 };
然后像这样:
1 Instance::Register("A", A::Create); 2 A* a = Instance::Create("A");
用字符串进行绑定,不是很方便,如果字符串拼写错了,在整个编译期不会有任何提示,而且字符串是没有代码自动提示的,这样出错率大大增加了,当然你可以复制/粘贴;如果我们能够将一个类型与数据进行绑定,通过类型获取与之相关的数据,一是类型有代码自动提示的(一般拼写前几个字符就可以完成整个输入),及时你自己拼写出错了,没有定义这个类型,编译器编译时也会报错。
1 typedef void*(*CreateCallback)(); // 创建函数的指针 2 class Instance { 3 public: 4 /** 注册 5 * @T 绑定的类型 6 * @callback 对象创建函数 7 */ 8 template<class T> 9 static void Register(CreateCallback callback) { 10 m_map.insert(std::make_pair(TypeDefine<T>, callback)); 11 } 12 13 /** 创建对象 14 * @T 创建对象的类型 15 * @return 创建的对象 16 */ 17 template<class T> 18 static T* Create() { 19 std::map<TypeDefineIndifiry, CreateCallback>::iterator iter = m_map.find(TypeDefine<T>); 20 return (T*)iter->second(); 21 } 22 23 private: 24 template<class T> 25 static void TypeDefine() {} 26 27 typedef void(*TypeDefineIndifiry)(); 28 static std::map<TypeDefineIndifiry, CreateCallback> m_map; 29 }; 30 std::map<Instance::TypeDefineIndifiry, CreateCallback> Instance::m_map;
接下来我们使用它:
1 Instance::Register<A>((CreateCallback)A::Create); 2 A* a = Instance::Create<A>();
时间: 2024-10-21 21:58:20