考虑如下代码:
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()函数中引入加锁和双检测机制。