自身特化的模板类:奇异循环模板
2018-02-02 08:40
169 查看
考虑如下代码:
上面的代码出现了2个问题:
1. 模板类的T,在整个模板类中并没有用到
2. 派生类以特化的模板类作基类,但是却用自身特化
实际上,这两个问题是为了实现同一个功能:
CBase是个模板类,但其有个static成员函数指针。那么:
class CDriverA : public CBase<int>
class CDriverB : public CBase<int>
如果是这样的代码,CDriverA与CDriverB会从同一个特化模板派生,则CDriverA与CDriverB会共享这个static成员函数指针。若不希望共享,可以采用这样的方式:
class CDriverA : public CBase<int>
class CDriverB : public CBase<double>
这样,CDriverA与CDriverB会从不同特化模板派生,从而CDriverA与CDriverB不共享这个static成员函数指针。
而实际上,由于T类型并没有在模板中使用,因此编译时这个T是什么并不重要。
于是:
class CDriverA : public CBase<CDriverA>
class CDriverB : public CBase<CDriverB>
这样的代码是合法的,并且其特化模板不同,从而CDriverA与CDriverB不共享这个static成员函数指针。
这就是用template实现多态的方法。
实际上,在模板类中,只要不涉及T的“实际实现”,即便代码中有T也是可以的。
不涉及实际实现的情况有以下几个:
1. T作函数返回值,包括T&与T*
2. T作函数成员变量,一定要是T*
示例代码:定义一个单件父类,从而派生类都是单件:
在派生类中:
上述代码就是一个奇异循环模板及其派生类。
注意:
1. 模板类的构造函数,拷贝构造函数,=运算符和析构函数不可以为private,因为private函数不能被派生类访问。考虑到是单件,定义为public并不合适,故定义为protected最合适。
2. 派生类中,由于是循环模板,所以相当于是令基类访问自身,所以要添加:
friend Singleton<MyClass>;
这样,基类就可以访问自身。
3. 派生类无法再派生。
4. 实际上,派生类的构造函数,拷贝构造函数,=运算符和析构函数都可以不再定义。
注意上面的代码仅适用于单线程。若要在多线程下使用,需要在getInstance()函数中引入加锁和双检测机制。
template<typename T > class CBase { public: CBase(); ~CBase(); static int* m_pValue; }; class CDriver : public CBase<CDriver> { public: int m_key; }
上面的代码出现了2个问题:
1. 模板类的T,在整个模板类中并没有用到
2. 派生类以特化的模板类作基类,但是却用自身特化
实际上,这两个问题是为了实现同一个功能:
CBase是个模板类,但其有个static成员函数指针。那么:
class CDriverA : public CBase<int>
class CDriverB : public CBase<int>
如果是这样的代码,CDriverA与CDriverB会从同一个特化模板派生,则CDriverA与CDriverB会共享这个static成员函数指针。若不希望共享,可以采用这样的方式:
class CDriverA : public CBase<int>
class CDriverB : public CBase<double>
这样,CDriverA与CDriverB会从不同特化模板派生,从而CDriverA与CDriverB不共享这个static成员函数指针。
而实际上,由于T类型并没有在模板中使用,因此编译时这个T是什么并不重要。
于是:
class CDriverA : public CBase<CDriverA>
class CDriverB : public CBase<CDriverB>
这样的代码是合法的,并且其特化模板不同,从而CDriverA与CDriverB不共享这个static成员函数指针。
这就是用template实现多态的方法。
实际上,在模板类中,只要不涉及T的“实际实现”,即便代码中有T也是可以的。
不涉及实际实现的情况有以下几个:
1. T作函数返回值,包括T&与T*
2. T作函数成员变量,一定要是T*
示例代码:定义一个单件父类,从而派生类都是单件:
template<typenameT>classSingleton { protected: Singleton(){}; Singleton(constSingleton&){}; Singleton&operator=(constSingleton&){}; ~Singleton(); public: staticT&GetInstance(); staticT*GetInstancePtr(); private: staticT* _instance; }; template<typenameT> T*Singleton<T>::_instance=nullptr; template<typenameT> Singleton<T>::~Singleton() { if(_instance!=nullptr) { delete _instance; _instance = nullptr; } } template<typenameT> T&Singleton<T>::GetInstance() { if(_instance==nullptr) { _instance=newT; } return*_instance; } template<typenameT> T*Singleton<T>::GetInstancePtr() { if(_instance==nullptr) { _instance=newT; } return_instance; }
在派生类中:
#include"Singleton.h" class MyClass : public Singleton<MyClass> { public: intgetNumber() { return 1199; } private: MyClass() {}; //MyClass(constMyClass&) {}; //MyClass& operator =(constMyClass&) {}; ~MyClass() {}; friend Singleton<MyClass>; //friend class auto_ptr<MyClass>; }
上述代码就是一个奇异循环模板及其派生类。
注意:
1. 模板类的构造函数,拷贝构造函数,=运算符和析构函数不可以为private,因为private函数不能被派生类访问。考虑到是单件,定义为public并不合适,故定义为protected最合适。
2. 派生类中,由于是循环模板,所以相当于是令基类访问自身,所以要添加:
friend Singleton<MyClass>;
这样,基类就可以访问自身。
3. 派生类无法再派生。
4. 实际上,派生类的构造函数,拷贝构造函数,=运算符和析构函数都可以不再定义。
注意上面的代码仅适用于单线程。若要在多线程下使用,需要在getInstance()函数中引入加锁和双检测机制。
相关文章推荐
- [C++基础]034_C++模板编程里的主版本模板类、全特化、偏特化(C++ Type Traits)
- ASP支持嵌套模板和循环标签的模板类
- [C++基础]034_C++模板编程里的主版本模板类、全特化、偏特化(C++ Type Traits)
- 奇异循环模板模式
- 模板-1-模板类的特化
- C++模板编程里的主版本模板类、全特化、偏特化(C++ Type Traits)
- C++模板的 主版本模板类、全特化、偏特化
- 模板类默认参数,模板类的特化,偏特化, 模板成员函数,模板的专用化例子
- [C++基础]034_C++模板编程里的主版本模板类、全特化、偏特化(C++ Type Traits)
- 关于模板类中模板成员函数在类定义外部特化报错,错为声明与定义不匹配
- C++模板编程中只特化模板类的一个成员函数
- C++模板编程中只特化模板类的一个成员函数
- underscore.js模板内循环输出
- 《C++ primer》学习笔记之二十七:当模板参数是另一个模板类的实例时写法要注意
- 类模板与模板类
- 双向循环链表-模板-自定义类型
- 利用模板偏特化实现编译期断言
- C++中模板的特化与偏特化
- 用汇编的眼光看C++(之缺省模板、特化模板)
- ThinkPHP_5模板循环标签