一、模板函数
定义:函数模板不是一个实在的函数,编译器不能为其生成可执行代码。定义函数模板后只是一个对函数功能框架的描述,当它具体执行时,将根据传递的实际参数决定其功能。
1、一般模板函数
面向对象的继承和多态机制有效提高了程序的可重用性和可扩充性。在程序的可重用性方面,程序员还希望得到更多支持。举一个最简单的例子,为了交换两个整型变量的值,需要写下面的 Swap 函数:
void Swap(int & x, int & y)
{
int tmp = x;
x = y;
y = tmp;
}
为了比较两个 double 型变量的值,还需要编写下面的 Swap 函数:
void Swap (double & xr double & y)
{
double tmp = x;
x = y;
y = tmp;
}
如果还要比较两个 char 型变量的值……都需要再编写 compare函数。而这些 compare函数除了处理的数据类型不同外,形式上都是一样的。能否只写一遍 compare函数,就能用来比较各种类型的变量的值呢?继承和多态显然无法解决这个问题。因此,“模板”的概念就应运而生了。
即只需编写一次函数模板,然后基于调用函数时提供的参数类型,C++编译器将自动产生相应的函数来处理该类型的数据。如以下代码实现比较 int、double类型的变量的值。 ?
#include <iostream>
using namespace std;
template<class Type>
int compare(const Type& v1, const Type& v2)
{
if (v1 < v2) return -1;
if (v1 > v2) return 1;
return 0;
}
int main() {
int i1 = 1, i2 = 2;
double d1 = 2.2, d2 = 1.1;
cout << compare(i1, i2) << endl;
cout << compare(d1, d2) << endl;
return 0;
}
?2.特化模板函数
定义:函数模板特化是在一个统一的函数模板不能在所有类型实例下正常工作时,需要定义类型参数在实例化为特定类型时函数模板的特定实现版本。目前的标准中,模板函数只能全特化,没有偏特化
#include <iostream>
#include <cstring>
using namespace std;
template<class Type>
int compare(const Type v1, const Type v2)
{
if (v1 < v2) return -1;
if (v1 > v2) return 1;
return 0;
}
template < > //函数模板特化
int compare<const char*>(const char* v1, const char* v2)
{
return strcmp(v1, v2);
}
int main() {
const char* a = "hi", * b = "world";
cout << compare(a, b) << endl;
return 0;
}
?函数模版的特化,当函数调用发现有特化后的匹配函数时,会优先调用特化的函数,而不再通过函数模版来进行实例化。
二、模板类Queue或Stack
定义:类模板的使用能使用户为类定义一种模式,使得类中的某些数据出成员、某些成员函数的参数、返回值或局部变量能取不同类型(包括系统预定义的和用户自定义的)
1、模板类(Queue,Stack)
格式:
template<模板参数表> class 类名 { ?? ?类成员声明 }; 类模板:
template<class Type> class QueueItem
{
QueueItem(const Type &t) :item(t), next(0){}
Type item;
QueueItem * next;
friend class Queue<Type>;
friend ostream& operator<<(ostream& os,const Queue<Type> &q);
public:
QueueItem<Type>* operator++()
{
return next;
}
Type & operator*()
{
return item;
}
};
模板参数表中参数可以声明为该模板类的友元类,如以上代码中的第6,7行的友元类,可通过typede或者using对实例化的累模板定义别名。
2、成员模板函数
在类模板中直接声明后实例化,如果需要在类模板意外定义其成员函数,则要采用 下面的形式(现在类模板中声明成员函数):
template<模板参数表> 类型名 类名<模板参数标识符列表>::函数名(参数表){} ?
template<class Type> c1ass Queue
{
public:
Queue():head(0),tail(0){}
Queue(const Queue& q):head(0),tail(0)
{
copy_items(q);
}
template<class It>
Queue(It beg, It end):head(0),tail(0)
{
copy_items(beg,end);
}
template<class It> void assign(It beg, It end);
};
template<class Type> template<class It>
void Queue<Type>::assign(It beg,It end)
{
destroy();
copy_items(beg,end);
}
3、模板特化
(1)模板函数特化:见内容一。
(2)模板成员函数特化:现为模板类Queue的成员函数push()和pop()方法实现特化,使其能够实现字符串的入队和出队操作。
template < >
void Queue<const char*>::push(const char* const& val)
{
char* new_item = new char[strlen(val) + 1];
strncpy(new_item, val, strlen(val) + 1);
QueueItem < const char*> * pt = new QueueItem < const char*>(new_item);
if (empty())
{
head = tail = pt;
}
else
{
tail->next = pt;
tail = pt;
}
}
template < >
void Queue<const char*>::pop()
{
QueueItem<const char*>* p = head;
delete head->item;
head = head->next;
delete p;
}
(3)模板类特化 ?
类模板的特化:与函数模板类似,当类模板内需要对某些类型进行特别处理时,使用类模板的特化。
模板参数的类模板特化的几种类型:
- 一是特化为绝对类型
- 二是特化为引用,指针类型
- 三是特化为另外一个类模板
template<>
class Queue<const char*>{//对字符类特化队列
public:
void Push(const char* str){real_queue.Push(str);}
void Pop(){real_queue.Pop();}
bool isEmpty() {return real_queue.isEmpty();}
string front() const {return real_queue.front();}
friend ostream & operator<<(ostream& os, Queue<const char*> &que){
os<<que.real_queue;
}
const string &front() const{return real_queue.front();}
private:
Queue<string> real_queue;
};
4.模板成员函数特化
template <>
void Queue<const char*>::Push( const char * const & str)//对字符类特化队列推进
{
char * pVal = new char[strlen(str)+1];
strcpy(pVal,str);
QueItem<const char*> * p = new QueItem<const char*>(pVal);
if(isEmpty())
{
head = tail = p;
}
else {
tail‐>next = p;
tail = p;
}
}
template<>
void Queue<const char* >::Pop()//对字符类特化队列清除
{
if(isEmpty())
{
return;
}
QueItem<const char*> * p = head;
head = head‐>next;
delete []p‐>item;
delete p;
}
三、模板类AutoPtr
1.构造函数
//构造函数
AutoPtr(T* pData);
template < class T>
AutoPtr<T>::AutoPtr(T* pData)
{
m_pData = pData;
//初始化用户数为1
m_nUser = new int(1);
}
2.析构函数
//声明周期结束时调用析构函数
~AutoPtr();
template < class T>
AutoPtr<T>::~AutoPtr()
{
decrUser();
}
template < class T>
void AutoPtr<T>::decrUser()
{
--(*m_nUser);
if ((*m_nUser) == 0)
{
//删除数据
delete m_pData;
//地址赋为空
m_pData = 0;
delete m_nUser;
m_nUser = 0;
}
}
3.拷贝构造函数
//拷贝构造函数
AutoPtr(const AutoPtr<T>& h);
template < class T>
AutoPtr<T>::AutoPtr(const AutoPtr<T>& h)
{
m_pData = h.m_pData;
m_nUser = h.m_nUser;
//用户数加1
(*m_nUser)++;
}
4.等号、->、*等运算符重载
//等号的重载
AutoPtr<T>& operator=(const AutoPtr<T>& h);
template < class T>
AutoPtr<T>& AutoPtr<T>::operator=(const AutoPtr<T>& h)
{
decrUser();
m_pData = h.m_pData;
m_nUser = h.m_nUser;
(*m_nUser)++;
}
//指针运算符重载(返回的指针允许被改变)
T* operator ->()
{
return m_pData;
}
//指针运算符重载(返回的指针不允许被改变)
const T* operator -> () const
{
return m_pData;
}
//返回一个对象(能使用成员运算符(".")来访问成员变量)
T& operator*()
{
return *m_pData;
}
const T& operator *() const
{
return *m_pData;
}
5.主函数调用AutoPtr
#include<iostream>
#include "queue.h"
#include "autoptr.h"
#include "CMatrix.h"
using namespace std;
int main() {
//创建一个CMatrix类的指针并交给智能指针类进行管理
AutoPtr<CMatrix> h1(new CMatrix);
double data[6] = { 1,2,3,4,5,6 };
//生成一个2行3列的数组
h1->Create(2, 3, data);
cout << *h1 << endl;
//h2(拷贝构造函数的使用)和h1指向的是同一个地方
AutoPtr<CMatrix> h2(h1);
(*h2).Set(0, 1, 10);
cout << *h1 << *h2 << endl;
return 0;
}
总结:
1.模板类本身未指定所使用的数据类型,不能单独编译模板类的实现。 只用在使用模板类的阶段,指定了模板中的数据类型,编译器才能正常编译。因此,在实际开发中,必须把实现全部写在头文件里面,把声明和实现分开的做法不可取。 2.模版不支持在局部函数中声明定义或使用。 3.自动类型推导,必须推导出一致的数据类型T,才可以使用模板必须要确定出T的数据类型,才可以使用。 4.模版类的定义和实现不能分开写在不同文件中,否则会导致编译错误。
|