C++类模板
目的:当同一种逻辑操作需要面对不同的数据类型时,如果不使用模板,就需要定义针对
不同数据类型的类,这样容易造成代码冗余和管理复杂。当采用了类模板时,就可以
定义一个逻辑模板,用相同的程序处理不同的数据类型,当需要处理指定数据类型时,
只要用该类型声明一个类模板实例。就使用进行相应的操作。
注:类模板的定义与实现要放在同一个文件中
一、类模板
普通类模板定义:
基本语法:
template<typename T1,typename T2...,非类型参数列表>
class 类模板名
{
类模板体
};
1、T1 T2的实参是数据类型名称,可以是系统预定义的。也可以是用户自定义的
2、非类型参数如:int size,这个size就是非类型参数,实参必须是常量表达式
,必须是编译期就能确定的值。
3、模板参数可有默认实参,如:
template <typename T=int , int size=100>
class Array
{
. . .
};
实例化: Array < > arryint; // 不可省略 < >
注:如果在定义类模板实体时,需要该类模板的一个实例作为参数之类的,并且该
实例的参数就是本模板的参数,就是本模板的参数,则可以直接使用类模板名
。
二、类模板成员函数
定义类模板的成员函数时,可以直接在类模板的内部定义,也可在类模板的外部定义,
注:类模板外部定义时,比一般的类的定义多了一个”template <模板参数>” 说明,
还有类名后加了一个 “<模板参数>”
基本语法:
template<模板参数列表>
返回类型 类模板名<模板参数列表>::函数名(函数参数列表)
{
函数体
}
1、类模板名后面的 “模板参数列表” 可以省略类型参数的typename
等关键字,以及非类型参数的类型关键字。
三、实例化类模板
类模板的实例化就是根据数据类型,实际的生成一个类对象。
与函数模板实例化不同的是,类模板在实例化时,没有参数推导机制,也就是没有
编译器不能根据给出的数据,自行推导实参数据类型,只能由开发者指定实参。
基本语法:
类模板名<模板实参列表> 实例名;
类模板实例化后,生成的实例类对象名,就是一个实际存在的类对象。
可以当作一般的类对象来使用。
类模板只在需要的时候才实例化,如程序中使用类模板的某个指针或
者引用,而没有通过这个指针或引用访问对象的数据成员或数据成员,则该类
模板不会被实例化。
如:
void Print(Stack<int> &vi)
{
Stack<int> *pvi=&vi; //并不会实例化
}
void Print(Stack<int> &vi)
{
Stack<int> *pvi=&vi;
cout<<pvi->Pop()<<endl;//模板会被实例化。
}
四、类模板的静态成员
和普通类一样,模板也可以有自己的静态成员,在类内声明,类外定义,
所不同的是,每种类型的类模板的实例都有自己的一组静态成员。静态
成员在类外定义(初始化)
基本语法:
template <模板参数列表>
类型名 类模板名<模板参数列表>::静态成员变量=初始值;
注:类模板名后的模板参数列表,可以省略关键字。
五、类模板的友元
相当于普通类的友元一样,可以通过类模板的对象(引用或者指针)访问类模板的私
有成员和保护成员。
在类模板中可以出现三种友元声明:
(1)普通非模板类或函数的友元声明,将友元关系授予明确指定的类或函数。
(2)类模板或函数模板的友元声明,授予对友元所有实例的访问权。
(3)只授予对类模板或函数模板的特定实例的访问权的友元声明。
六、类模板的特化
有的时候某些类型不能直接用来实例化类模板,或者说直接实例化不能满足需要,
此时就要针对这种类型进行特化,包括特化(即全特化)和偏特化。
1、全特化
目的:为了针对特殊的类型进行特殊处理,重新修改成新的逻辑,定义一个
新的特化的类模板。
特点:template后没有形参说明,类模板名加了模板实参列表。
基本语法:
template<>
class 类模板名<模板实参列表>
{
新的逻辑操作
};
2、偏特化(部分特化)
特点:template后的模板形参的形参没有全部不写,类模板名后添加了参数说明。
或者两个都
基本语法:
template<模板形参列表>
class 类模板名<模板形参表>
{
};
注:类模板名后的形参,可以把template后的形参当作已知的类型,组合成新
的类型来作为类模板的参数。然后通过实例化template的形参,
最后进一步确定类模板的实参。
注:所有特化类模板之前,一定先需要定义同名的非特化类模板,否则会有编译错误
七、类模板的匹配规则
类模板的匹配遵循使用“特化程度最高”原则,模板参数最准确匹配的拥有最
高的实例化优先权。
八、三种类型的特化
1、全特化
2、特化为引用和指针
3、特化为另外一个模板类
测试程序:
#include <iostream>
using namespace std;
//类模板的基本实现
//使用类模板实现对不同数据的栈存储管理
template <typename T,int size>
class Stack
{
public:
Stack():top(0),count(0)
{
}
int Push(T data)//入栈操作
{
if(top<size)//栈没有满 top-size时,表示存满
{
stack_data[top]=data;
top++;
return 0;//返回0代表入栈成功
}
return 1;
}
int Pop();//出栈操作
T getTopValue()//获取栈顶元素
{
if(top>0)
{
return stack_data[--top];
}
return T(1);
}
int getTop()
{
return this->top;//返回栈顶指针
}
void PrintStack();
private:
T stack_data[size];
int top;//始终指向栈顶元素的上一个位置
int count;
};
template <typename T,int size>
int Stack<T,size>::Pop()
{
if(top>0)
{
top--;
return 0;
}
return 1;
}
template <typename T,int size>
void Stack<T,size>::PrintStack()
{
for(int temp=top;temp>0;temp--)
{
cout<<stack_data[temp-1]<<"->";
}
cout<<endl;
}
int main()
{
Stack<int,10> int_stack;//使用类模板实例化一个类对象
// cout<<int_stack.getTop()<<endl;
// cout<<(int)int_stack.getTopValue()<<endl;
cout<<"Int型数据:"<<endl;
for(int i=0;i<11;i++)
{
if(int_stack.Push(i+1)==1)//入栈操作
{
cout<<"栈已满"<<"元素:"<<i+1<<"入栈失败"<<endl;
}
}
int_stack.PrintStack();
int_stack.Pop();
int_stack.Push(11);
int_stack.PrintStack();
cout<<"栈顶元素:"<<int_stack.getTopValue()<<endl;
cout<<"Double型数据:"<<endl;
Stack<double,20> double_stack;
for(double i=0;i<22.0;i++)
{
if(double_stack.Push(i+2.0)==1)//入栈操作
{
cout<<"栈已满"<<"元素:"<<i+2.0<<"入栈失败"<<endl;
}
}
double_stack.PrintStack();
double_stack.Pop();
double_stack.Push(22.0);
double_stack.PrintStack();
cout<<"栈顶元素:"<<double_stack.getTopValue()<<endl;
return 0;
}
输出结果:
Int型数据:
栈已满元素:11入栈失败
10->9->8->7->6->5->4->3->2->1->
11->9->8->7->6->5->4->3->2->1->
栈顶元素:11
Double型数据:
栈已满元素:22入栈失败
栈已满元素:23入栈失败
21->20->19->18->17->16->15->14->13->12->11->10->9->8->7->6->5->4->3->2->
22->20->19->18->17->16->15->14->13->12->11->10->9->8->7->6->5->4->3->2->
栈顶元素:22