目录
一、模板函数
1.概念
2.一般模板函数
3.特化模板函数
二、模板类
1.概念
2.模板类(Queue,Stack)
2.1Queue
2.2Stack
3.成员模板函数
3.模板类特化
3.1全特化
3.2偏特化
三、模板类AutoPtr
1.构造函数
2.析构函数
3.拷贝构造函数
4.等号、->、*等运算符重载
5.主函数调用AutoPtr
模板函数不是一个实在的函数,编译器不能为其生成可执行代码。定义模板函数后只是一个对函数功能框架的描述,当它具体执行时,将根据传递的实际参数决定其功能。
以一个简单的Swap函数为例,其功能为交换两个变量的值。
void Swap(int & x, int & y)
{
int tmp = x;
x = y;
y = tmp;
}
上面的代码能实现交换功能,但是只能交换整数型即int类型,那么如果要交换浮点型或者字符型又需要另外写以下代码。
//浮点型
void Swap(double & x, double & y)
{
int tmp = x;
x = y;
y = tmp;
}
//字符型
void Swap(char & x, char & y)
{
int tmp = x;
x = y;
y = tmp;
}
为了减少我们的代码量,此时就可以使用模板函数。所谓“模板”,就是可以用一个模子做出多种类似的东西,那么模板函数就是可以用一个函数实现对多种类型值的操作。
模板函数写法如下:
template
返回值类型 模板名(形参表)
{
函数体
}
还是以Swap函数为例,实现代码为:
#include
using namespace std;
template
void Swap(T & x, T & y)
{
T tmp = x;
x = y;
y = tmp;
}
int main()
{
int n = 2, m = 8;
Swap(n, m); //编译器自动生成 void Swap (int &, int &)函数
cout<<"n="<
仅靠一个模板函数便实现了整数型、浮点型和字符型三种类型值的交换,运行截图如下:
尽管模板函数能够对多种类型值进行操作,但是不同类型值在有些操作时需要先进行一定处理即特化,比如字符型是无法直接进行大小比较的,但我们可以比较其字符串长短,下面我们以Compare函数为例。
#include
#include
using namespace std;
//函数模板
template
bool Compare(T t1,T t2){
return t1==t2;
}
template<> //函数模板特化
bool Compare(char *t1,char *t2){
return strcmp(t1,t2)==0;
}
int main(int argc, char* argv[])
{
int a = 2;
int b = 2;
int c = 8;
double d = 2.8;
double e = 2.8;
double f = 8.2;
char str1[] = "abc";
char str2[] = "abc";
char str3[] = "def";
cout<
如果类型值相等输出为1,反之则输出为0。运行截图如下:
可见经过特化之后的Compare函数除了能对数值进行大小比较还可以对字符型进行比较操作。需要注意的是模板函数只有全特化,没有偏特化。
一个类模板(类生成类)允许用户为类定义个一种模式,使得类中的某些数据成员、默认成员函数的参数,某些成员函数的返回值,能够取任意类型(包括系统预定义的和用户自定义的)。
如果一个类中的数据成员的数据类型不能确定,或者是某个成员函数的参数或返回值的类型不能确定,就可以将此类声明为模板,它的存在不是代表一个具体的、实际的类,而是代表一类 类。
模板类的作用和模板函数的相同,都是为了减少代码量方便操作,这里就不再赘述。
模板类的写法:
template <类型形参表>
class <类名>
{ //类说明体 };
template <类型形参表>
<返回类型> <类名> <类型名表>::<成员函数1>(形参表)
{ //成员函数定义体 }
template <类型形参表>
<返回类型> <类名> <类型名表>::<成员函数2>(形参表)
{ //成员函数定义体 }
…
template <类型形参表>
<返回类型> <类名> <类型名表>::<成员函数n>(形参表)
{ //成员函数定义体 }
在C++标准库(STL)中有栈和队列的类模板,因此可以直接使用。
队列的特点:先进先出,只能对队头和队尾的元素进行操作。
queue的函数组成主要有:
queue.size():返回队列中元素个数;
queue.push():会将一个元素置入queue中;
queue.front():会返回queue内的第一个元素(也就是第一个被置入的元素);
queue.back():会返回queue中的最后一个元素(也就是最后被插入的元素);
queue.pop():会移除queue内的第一个元素(也就是第一个被置入的元素);
使用队列,要先包含头文件 : #include
#include
#include
using namespace std;
int main(){
queue queue;
int num;
cout<<"入队:";
for(int i = 1;i<10;i++){
queue.push(i);
cout<
运行截图:
栈的特点:先进后出,只能对栈顶的元素进行操作。
stack的函数组成主要有:
stack.push():往栈头添加元素;
stack.pop():从栈头移除第一个元素;
stack.top():返回栈顶元素;
stack.empty():判断堆栈是否为空,栈空返回true,栈非空返回false;
stack.size():返回堆栈的大小;
使用栈之前,要先包含头文件 : #include
#include
#include
using namespace std;
int main(){
stack stack;
int num;
cout<<"入栈:";
for(int i = 1;i<10;i++){
cout<
运行截图:
成员模板函数在使用时有以下需要注意的地方:
普通类的成员函数模板不管是普通类还是模板类,成员函数都可以是函数模板,称为“成员函数模板”,但不可以是虚函数,否则编译器报错。
类模板的模板参数必须用<>指定,成员函数模板(函数模板)的模板参数可以自动推导。
类模板的成员函数(普通成员函数/函数模板)只有为程序所用时(代码对函数或函数模板进行调用时)才实例化。 如果某函数从未使用,则不会实例化该函数。
//普通类
class A{
public:
template //成员函数模板
void myfunc(T tempVal)
cout << tempval <
class A{
public:
template
A(T2 v1, T2 v2){
//构造函数模板
}
template //成员函数模板
void myfunc(T tempVal)
cout << tempval < a(1,2); //类模板的模板参数必须指定,函数模板自动推导。
A a(1.1, 2.2);
a.myfpt(); //调用时,才实例化该函数
成员函数外部实现
template //先跟类模板的模板函数列表
template //构造函数自己的模板函数列表
A::A(T2 v1, T2 v2){
cout << v1 << v2 << endl; //构造函数模板
}
模板函数特化和模板类特化的区别:模板函数只能全特化,而模板类既有全特化又有偏特化。
全特化即对所有模板类型都进行特化。
#include
using namespace std;
template
class A{
public:
void function(T1 value1, T2 value2){
cout<<"value1 = "<
class A{ // 类型明确化,为全特化类
public:
void function(int value1, double value2){
cout<<"intValue = "< a;
a.function(28, 28.8);
return 0;
}
运行截图:
偏特化即对模板类型做一些限制特化。
#include
using namespace std;
template
class A{
public:
void function(T1 value1, T2 value2){
cout<<"value1 = "<
class A{ // 部分类型明确化,为偏特化类
public:
void function(T value1, double value2){
cout<<"charValue = "< a;
a.function('a', 28.8);
return 0;
}
运行截图:
template
AutoPtr::AutoPtr(T* pData)
{
m_pData = pData;
m_nUser = new int(1);
}
~AutoPtr()
{
decrUser();
}
void decrUser();
template
void AutoPtr::decrUser()
{
--(*m_nUser);
if ((*m_nUser) == 0)
{
delete m_pData;
m_pData = 0;
delete m_nUser;
m_nUser = 0;
}
}
template
AutoPtr::AutoPtr(const AutoPtr& h)
{
m_pData = h.m_pData;
m_nUser = h.m_nUser;
(*m_nUser)++;
}
AutoPtr& operator=(const AutoPtr& h);
T* operator->()
{
return m_pData;
}
T& operator*()
{
return *m_pData;
}
const T& operator *()const
{
return *m_pData;
}
const T* operator ->()const
{
return m_pData;
}
template
AutoPtr& AutoPtr::operator=(const AutoPtr& h)
{
decrUser();
m_pData = h.m_pData;
m_nUser = h.m_nUser;
(*m_nUser)++;
}
#include
#include
#include "autoptr.h"
#include "CMatrix.h"
using namespace std;
int main()
{
AutoPtr h1;
double data[6] = {1,2,3,4,5,6};
h1->Create(2,3,data);
cout << *h1 << endl;
AutoPtr h2(h1);
(*h2).Set(0,1,10);
cout << *h1 << endl << *h2;
}
运行截图: