1.类模板与模板类的概念
⑴ 什么是类模板 一个类模板(也称为类属类或类生成类)允许用户为类定义一种模式,使得类中的某些数据成员、默写成员函数的参数、某些成员函数的返回值,能够取任意类型(包括系统预定义的和用户自定义的)。
如果一个类中数据成员的数据类型不能确定,或者是某个成员函数的参数或返回值的类型不能确定,就必须将此类声明为模板,它的存在不是代表一个具体的、实际的类,而是代表着一类类。
⑵ 类模板定义 定义一个类模板,一般有两方面的内容:
A. 首先要定义类,其格式为:
template <class T>
class foo
{
……
}
foo 为类名,在类定义体中,如采用通用数据类型的成员,函数参数的前面需加上T,其中通用类型T可以作为普通成员变量的类型,还可以作为const和static成员变量以及成员函数的参数和返回类型之用。例如:
template<class T>
class Test{
private:
T n;
const T i;
static T cnt;
public:
Test():i(0){}
Test(T k);
~Test(){}
void print();
T operator+(T x);
};
B. 在类定义体外定义成员函数时,若此成员函数中有模板参数存在,则除了需要和一般类的体外定义成员函数一样的定义外,还需在函数体外进行模板声明
例如
template<class T>
void Test<T>::print(){
std::cout<<"n="<<n<<std::endl;
std::cout<<"i="<<i<<std::endl;
std::cout<<"cnt="<<cnt<<std::endl;
}
如果函数是以通用类型为返回类型,则要在函数名前的类名后缀上“<T>”。例如:
template<class T>
Test<T>::Test(T k):i(k){n=k;cnt++;}
template<class T>
T Test<T>::operator+(T x){
return n + x;
}
C. 在类定义体外初始化const成员和static成员变量的做法和普通类体外初始化const成员和static成员变量的做法基本上是一样的,唯一的区别是需在对模板进行声明,例如
template<class T>
int Test<T>::cnt=0;
template<class T>
Test<T>::Test(T k):i(k){n=k;cnt++;}
⑶ 类模板的使用 类模板的使用实际上是将类模板实例化成一个具体的类,它的格式为:类名<实际的类型>。
模板类是类模板实例化后的一个产物。说个形象点的例子吧。我把类模板比作一个做饼干同的模子,而模板类就是用这个模子做出来的饼干,至于这个饼干是什么味道的就要看你自己在实例化时用的是什么材料了,你可以做巧克力饼干,也可以做豆沙饼干,这些饼干的除了材料不一样外,其他的东西都是一样的了。
2.类模板的派生
可以从类模板派生出新的类,既可以派生类模板,也可以派生非模板类。派生方法:
⑴ 从类模板派生类模板可以从类模板派生出新的类模板,它的派生格式如下例所示:
template <class T>
class base
{
……
};
template <class T>
class derive:public base<T>
{
……
};
与一般的类派生定义相似,只是在指出它的基类时要缀上模板参数,即base<T>。
⑵ 从类模板派生非模板类 可以从类模板派生出非模板类,在派生中,作为非模板类的基类,必须是类模板实例化后的模板类,并且在定义派生类前不需要模板声明语句:template<class>。例如:
template <class T>
class base
{
……
};
class derive:public base<int>
{
……
};
在定义derive类时,base已实例化成了int型的模板类。
下面是使用介绍:
这几天看了下模板 ,以下是个人的些心得,高手见笑了
1.class templates 的成员函数 在调用和取地址时实例化,在类实例化时不会自动实例化
2.class templates 的友元申明
a.非模版类型
friend class man;
friend void f(void);
friend void man::f(void);
b.一对一的模版友元
friend class man<T>;
friend void f<T>( man<T>);
friend void man<T>::f();
c.一对多的模版友元
template <typename T>
friend class man;
template <typename T>
friend void f( T);
template <typename T>
friend void man<T>::f();
3.类模版的静态成员并不在模版定义时实例化,在类模版实例化时实例化,对应1个类型
template <typename T>
int man<T>::size=10;
而成员函数应为
template <typename T>
void man<T>::f(void)
{
..........................................
}
4.成员模版 可以是普通类和 模版类
如:
class woman
{
public:
template <typename T>
class man
{
public:
print(T a)
{
cout<<a;
}
};
template <typename T2>
void print(T2 a)
{
cout<<a;
};
protected:
private:
};
1个好的例子
template <typename T> class A
{ public:
template <typename T1>
A& operator =(const A<T1> & );
}
//如此 则A 的对象 可以用其他型的对象赋值了 在使用时才实例化 但是其定义比较骇人
template <typename T>
template<typename T1>
A<T>& A<T>::operator=(const A<T1>& a)
{ ...........................}
6.类模版的特化
特化1个类模板(所有成员必须特化包括静态类数据成员(vc),但友元模板(1:1)可以
不特化,当作一个显示实参的函数模板处理即可) 或 特化1个模板 成员函数
模板成员函数特化是定义必须出现在实现文件(cpp)中
语法为
void man<int>::f(void)
{
............................
}
类特化
template <>
class man<int>
{
......//中所有类型参数以int 替代
};
man<int>::man()
{
......
}
void man<int>::f(void)
{
}
注意 类成员中
static const 有序类型可以在体内初始化static const int a=10;
仅仅static 必须在体外
static int a;
int woman::a=10;
非 特化
template <typename T>
int man<T>::size=10;
特化
int man<char>::size=10;
7偏特化
类模板有1个以上模版参数(类型参数,非类型参数)
某些模版参数 被实际的型和值取代(注意是 取代 ,不是增加,减少)
如:
//////////////////头文件
template <typename T1,typename T2,int size>
class wc
{
public:
wc()
{
cout<<"\n int T1,T2, int";
}
protected:
private:
};
template <typename T,int size>
class wc<T* ,T,size>
{
public:
wc()
{
cout<<"\n in T *, T ,size";
}
protected:
private:
};
template <typename T,int size>
class wc<T* ,float,size>
{
public:
wc()
{
cout<<"\n in T* ,float,size";
}
protected:
private:
};
template <typename T>
class wc<T* ,float,80>
{
public:
wc()
{
cout<<"\n in T* ,float,80";
}
protected:
private:
};
///////头文件
//////main()
wc<int,float,10> wc1;
wc<int* ,int ,20> wc2;
wc<int*,float,39> wc3;
wc<int*,float,80>wc4;
///////main() ,在vc 7 上通过
//////main()
8.预设模板参数(只有在类模板中才指定 ,1 . 明确指定 a<int,int> ,直接定义时 man<> kkk;2. a<T,T> )
template <typename T,typename T1=float>
class man
{
..........
}
写到这里突然想到
class A {
virtual void print();
};
class B:public A {
virtual void print();
};
B b;
A *a =&b;
a->print();//调用 B的,此时 print()中的this 指针是 B * const,使用B 的接口 ;
9.双重模板参数(很有用)
template <typename T,typename G =man<T> >
class baby
{
public:
baby ()
{
cout<<"\n in baby";
}
protected:
private:
};//类型参数可以是 类类型
baby<int, man<float> > b; //ok
////////////////////////
template <typename T,template <typename G> class TT>//可以省略为 template <typename T ,template<typename> class TT>
class son
{
public:
TT<T> * a;
son()
{
a=0;
cout<<"\n in son";
}
protected:
private:
};
son<int, man> b;//则就含有了 man<int > * a;
TT 是模板占位符号,是一个带1个类型参数的类模板,如果其有默认模板参数 则忽略掉
所以 GG若带默认 的模板 应该如下
template <typename T, template <typename T2, typename= man<T2> > class GG>
class son
{
}
///////// 类模板类型参数 同时还可以是 类模板
10. 字符串常量在模板中的注意事项
char * a="hi ,friend ";///"hi, friend" 文字常量 是 const char [10] 类型 左值转换到 const char *
限定转换到 char *,
template <typename T〉
ostream& operator <<(ostream & os, const T & b);//注意只有const 才能接受临时变量
cout<<"hi,friend";
cout<<"bad";
/////2个不同的模板实例
typeid(变量).name();///直接打印出变量类型
写到这,闲扯下:
栈区,堆区,全局(静态)区,文字常量,代码区,5个大区,这个是我听说的
////////////////////////////////////////////////
11.模板的编译模型
在实例化点必须能找到定义//原则
只能使用包含模式了,
a..h. cpp 在 .h最后一行包含进 cpp
b. 全部在h 或 cpp
分离模式,目前支持的export 关键字i的编译器很少(vc.net 不支持)
12.
函数指针类型也能做模板类型参数
13.
模板参数可以是个类模板
template <typename T>
class a
{
};
a< vector <int> >;
template <typename T, typename T2,template <typename T,typename T2> class con >
void add(con<T,T2>,con<T,T2>)
{
cout<<"ok";
}
add(a,b)//特殊在于 需要推导,vc 7 可以从 类模板中推导出参数类型
template <typename T,template <typename T2,typename T3> class con >
class ggoo
{
public:
con<T,T> a;
con<int,int> b;
protected:
private:
};
ggoo<int,double, b> kk;//b<int,double>, con 的模板参数可以任意