一、函数模板
template <typename T> T max (T a, T b){
return a>b ? a : b;
}
编译器编译到max(i1,i2)时,会根据模板实参生成一个具体函数,叫做函数模板实例化
实例化在编译阶段完成,在函数调用处发生
函数模板与函数重载
调用顺序:首先检查是否存在重载函数,若匹配成功则调用该函数,否则再去匹配函数模板
二、类模板
1.类模板定义
template <typename T>//模板形参表:用来说明一个或多个类型形参和普通形参;多个模板参数用逗号分隔
class TestClass {
public:
T buffer[10];
T getData(int j);
};
template <typename T>
T TestClass<T>::getData(int j) {
return *(buffer + j);
};
2.类模板实例化:提供模板参数,以便编译器可以生成实际的类
类模板实例化只在需要类定义的时候才会发生!
void main() {
TestClass<char> ClassInstA;
TestClass<double> ClassInsB;
TestClass<int> ClassInsC;
}
函数声明,不需要类定义,不需要实例化
定义引用,不需要类定义,不需要实例化
定义指针,不需要类定义,不需要实例化
定义类对象,需要类定义,需要实例化
访问指针指向的类对象,需要类定义,需要实例化
sizeof需要有类定义才能知道对象大小,需要类定义,需要实例化
template<typename Type> class Graphics {};
void f1(Graphics<char>);
class Rect {
Graphics<double>& rsd;
Graphics<int> si;
};
void main() {
Graphics<char>* sc;
f1(*sc);
int iobj = sizeof(Graphics<string>);
}
类模板也可以支持普通参数(非类型参数)
template <typename T, int i>
class TestClass {
public:
T buffer[i];
T getData(int j);
};
template <typename T, int i>
T TestClass<T,i>::getData(int j) {
return *(buffer + j);
};
void main() {
TestClass<char, 5> ClassInstA;
char cArr[6] = "abcde";
for (int i = 0; i < 5; i++)
ClassInstA.buffer[i] = cArr[i];
for (int i = 0; i < 5; i++) {
char res = ClassInstA.getData(i);
cout << res << " ";
}
}
注意类外定义成员函数时参数列表需一致,以及声明方式
template <typename T, int i>
T TestClass<T,i>::getData(int j) {
return *(buffer + j);
};
3.类模板的静态成员
类模板的静态成员是模板实例化类的静态成员,对于每一个实例化类,其所有的对象共享其静态成员
类TA和类TA可以认为是两个不同的类,他们的类对象不共享静态变量m_t1 !
#include<iostream>
using namespace std;
template<typename T>class TA {
public:
static int m_t1;
static T m_t2;
};
template<typename T> int TA<T>::m_t1 = 100;
template<typename T> T TA<T>::m_t2 = 0;
int main() {
TA <int> iobj1, iobj2;
TA <double> dobj1, dobj2;
iobj1.m_t1++; iobj1.m_t2++;
iobj2.m_t1++; iobj2.m_t2++;
dobj1.m_t1++; dobj1.m_t2++;
dobj2.m_t1++; dobj2.m_t2++;
cout << TA<int>::m_t1 << " " << TA<int>::m_t2 << endl;
cout << dobj1.m_t1 << " " << dobj2.m_t2 << endl;
return 0;
}
结果:
102 2
102 2
4.类模板的成员函数
类模板的所有成员函数都是函数模板
类模板实例化时成员函数并不自动被实例化,只有函数调用时才被实例化
template<typename T> class MyStack {
public:
void create();//虽然没有用到模板类型 T,但create()仍然是函数模板
void push(const T item);
};
template<typename T> void MyStack<T>::create() {};
template<typename T> void MyStack<T>::push(const T item) {};
int main()
{
MyStack<int> s1;
s1.create();
}
类模板的成员函数也可以是另外一个函数模板
template<typename T> class MyStack {
public:
template<typename U> void create(U a);
};
template<typename T> template<typename U> void MyStack<T>::create(U a){};
int main()
{
MyStack<int> s1;
s1.create(2.5);
}
普通类的成员函数也可以是函数模板
5.类模板与友元函数
如果类模板的友元函数是普通函数,则友元函数是该类模板任意类实例的友元
void create() {}
template<typename T> class MyStack {
public:
friend void create();
};
如果友元函数是与类模板无关的模板函数,则友元函数的任意函数实例是任意类实例的友元
template<typename U> void create(U x) {};
template<typename T> class MyStack {
public:
friend template<typename U> void create(U x);
};
例如:函数模板的实例 void create(int) 和 void create(char)都是类模板实例 MyStack, MyStack的友元函数
友元函数是与类模板有关的模板函数,则友元函数只是该类模板特定类实例的友元
template<typename U> void create(U x) {};
template<typename T> class MyStack {
public:
friend void create<T>(T x);
};
6.类型参数检测与特例版本
类模板的类型参数往往在实例化时不允许用任意的类(类型)作为“实参"
template <typename T>
class stack {
T data[20];
int top{ 0 };
public:
void showtop(void);
};
template <typename T> void
stack<T>::showtop(void) {
cout << "Top_Member:" << data[top] << endl;
}
class complex {
public:
double real, image;
};
//自定义类,表示复数
void main() {
stack<int>i1;
stack<char>c1;
stack<float>f1;
stack<complex>cp1;//ERROR! 实例化为complex型,但showtop函数里的输出操作符 << 并不支持complex类型
}
解决方案一:对complex类进行<<运算符重载
解决方案二:在statck类模板里填加showtop函数的特例版本专门支持complex类
void stack<complex>::showtop() {
cout<<"Top_Member:"<<data[top].real<<' '<<data[top].image<<endl;
}
7.类模板的继承与派生
一般类(其中不使用类型参数的类)作基类,派生出类模板(其中要使用类型参数)
class CB { //基类CB 为一般类(其中不使用类型参数)
...
};
//派生类CA为类模板,使用了类型参数T
template <typename T> class CA :public CB {
T t; //派生类新加的成员
public:
...
};
类模板作基类,派生出新的类模板,但仅基类中用到类型参数T,而派生的类模板中不使用T
template <typename T> class CB {
T t;
public:
T gett() { return t; }
};
template <typename T> class CA : public CB<T> {
double t1; //派生类的新成员并不使用 T
T t1;//派生类新填加的成员也使用了基类的模板类型T
};
类模板作基类,派生出新的类模板,但基类和派生类使用的类型参数不同
template <typename T2> class CB {
T2 t2; //数据类型为T2
public:
...
};
template <typename T1, typename T2>class CA :
public CB<T2> {
T1 t1; //数据类型为T1
};