一、模板
1.为什么要使用模板?
??C++最重要的特性之一就是代码重用,为了实现代码重用,代码必须具有通用性。通用代码需要不受数据类型的影响,并且可以自动适应数据类型的变化。这种程序设计类型称为参数化程序设计。 ??因此C++就有了“模板”这一名词,模板是C++支持参数化程序设计的工具,通过它可以实现参数化多态性。所谓参数化多态性,就是将程序所处理的对象的类型参数化,使得一段程序可以用于处理多种不同类型的对象。
2.模板定义:
??模板是实现代码重用机制的一种工具,它可以实现类型参数化,即把类型定义为参数,从而实现了真正的代码可重用性。
3.模板分类:
??模板大致分为两类函数模板和类模板。 ??函数模板针对参数类型不同的函数; ??类模板仅针对数据成员和成员函数类型不同的类。
4.使用模板目的:
??让程序员编写与类型无关的代码。 ??注意:模板的声明或定义只能在全局,命名空间或类范围内进行。即不能在局部范围,函数内进行,如不能在main函数中声明或定义一个模板。
二、函数模板
1.什么是函数模板:
??函数模板不是一个实在的函数,编译器不能为其生成可执行代码。定义函数模板后只是一个对函数功能框架的描述,当它具体执行时,将根据传递的实际参数决定其功能。
2.函数模板的定义形式:
template<class 类型参数1, class类型参数2, ...>
返回值类型名 函数名(参数表)
{
}
其中class也能用typename替代
template <typename 类型参数1, typename 类型参数2, ...>
3.一般模板函数用法(compare)
??举一个简单的例子,为了比较两个数a,b的值,并得出最大值需要用到以下的compare函数。 若比较的数值是整形,则:
int compare(int& a, int& b) {
if (a < b) {
return b;
}
else if (b < a) {
return a;
}
else
return a;
}
若比较的数值是double浮点型,则:
double compare(double& a, double& b) {
if (a < b) {
return b;
}
else if (b < a) {
return a;
}
else
return a;
}
??可以看到针对不同类型的变量我们都得重新再编写一次compare函数,非常的麻烦。 ??那么,有什么办法可以让我们只需要编写一次compare函数就能够比较不同变量类型的数值呢?这时候就要用到我们的模板函数了。
根据模板函数的定义形式我们重新对compare函数进行定义:
T compare(T& a, T& b) {
if (a < b) {
return b;
}
else if (b < a) {
return a;
}
else
return a;
}
接下来通过main函数运行不同的变量参数:
int main() {
int a = 1;
int b = 2;
int max_int = compare(a, b);
cout <<"int整型最大数为:"<<max_int<< endl;
double c = 1.11;
double d = 1.12;
double max_double = compare(c, d);
cout << "double浮点型最大数为:" << max_double << endl;
}
运行截图: 可以看到通过模板函数我们只需要在main函数定义好类型便能直接调用compare函数。
4.特化模板函数用法
??使用模板函数时并不是所有的变量类型都适用,有时候会遇到一些特殊的类型需要特殊处理,不能直接使用当前的模板函数,所以此时我们就需要对该类型特化出一个模板函数(就是写出一个模板函数专门给该类型使用) ??模板还分为全特化和偏特化两类,但函数模板只有全特化,因为偏特化的功能可以通过函数的重载完成。 模板函数的全特化:
template<>
char compare<char>(char& a1, char& b1) {
if (a1 < b1) {
return b1;
}
else if (b1 < a1) {
return a1;
}
else
return a1;
}
三、类模板(Queue)
1.什么是类模板:
??使用template关键字不但可以定义函数模板,也可以定义类模板,类模板代表一族类,是用来描述通用数据或处理方法的机制,它使类中的一些数据成员和成员函数的参数或返回值可以取任意的数据类型。类模板可以说是用类生成类,减少了类的定义数量。
2.类模板的定义形式:
template <类型形式及参数>
class 类模板名{
}
若要在类模板以外定义其成员函数,则要采用以下的形式:
template <类型形式及参数>
类型名 类名<模板参数标识符列表>::函数名(参数表)
3.模板类的用法
(一)、Queue,QueueItem类
??如下例子所示,定义了模板类Queue和模板类QueueItem
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;
}
};
template<class Type>
class 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);
Queue& operator=(const Queue&);
~Queue() { destroy(); }
Type& front() { return head->item; }
const Type& front() const { return head->item; }
void push(const Type&);
void pop();
bool empty() const { return head == 0; }
friend ostream& operator<<(ostream& os, const Queue<Type>& q) {
os << "< ";
QueueItem<Type>* p;
for (p = q.head; p; p = p->next) {
os << p->item << " ";
}
os << ">";
return os;
}
const QueueItem<Type>* Head() const { return head; }
const QueueItem<Type>* End() const { return (tail == NULL) ? NULL : tail->next; }
private:
QueueItem<Type>* head;
QueueItem<Type>* tail;
void destroy();
void copy_items(const Queue&);
template<class It> void copy_items(It beg, It end);
};
(二)、成员模板函数
??代码如下,创建了三个成员模板函数,destroy() ,pop() 和push() ,对应的功能分别是清空所有数据,删除队列最后一个数据以及为队列添加数据
template<class Type>void Queue<Type>::destroy()
{
while (!empty()) {
pop();
}
}
template<class Type> void Queue<Type>::pop() {
QueueItem<Type>* p = head;
head = head->next;
delete p;
}
template<class Type> void Queue<Type>::push(const Type& val) {
QueueItem<Type>* pt = new QueueItem<Type>(val);
if (empty()) {
head = tail = pt;
}
else {
tail->next = pt;
tail = pt;
}
}
(三)、模板特化
??前面已经讲解了一下什么是模板特化,模板函数的特化又与模板类的特化有所不同。模板函数只能全特化,而模板类既有全特化又有偏特化。 ??全特化即将所有模板类型都进行特化,例如将上述程序的Type用int来代替; ??偏特化即对模板类型做一些限制,偏特化分为两种,部分特化和类型范围限制。 模板成员函数全特化:
template<>
inline void Queue<const char*>::push(const char* const& val);
template<>
inline void Queue<const char*>::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;
}
模板类特化:
template<>
class 类名<特化类型>
{
类成员声明
};
4.运行测试Queue类
void TestQueue()
{
Queue<int> qt;
double d = 3.3;
qt.push(1);
qt.push(d);
qt.push(10);
cout << endl;
cout << qt;
short data[5] = { 0,3,6,9 };
Queue<int> qi(data, data + 5);
cout << endl;
cout << qi;
vector<int> vi(data, data + 5);
qi.assign(vi.begin(), vi.end());
cout << endl;
cout << qi;
Queue<const char*> q1;
q1.push("I'm");
q1.push("come");
q1.push("from");
q1.push("JMU");
cout << endl;
cout << q1;
Queue<const char*> q2(q1);
cout << q2;
}
运行截图:
四、类模板实现(AutoPtr类)
1.构造函数
template<class T>
AutoPtr<T>::AutoPtr(T* pData)
{
m_pData = pData;
m_nUser = new int(1);
}
2.析构函数
~AutoPtr()
{
decrUser();
}
void 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.拷贝构造函数
template<class T>
AutoPtr<T>::AutoPtr(const AutoPtr<T>& h)
{
m_pData = h.m_pData;
m_nUser = h.m_nUser;
(*m_nUser)++;
}
4.等号、->、*等运算符重载
智能指针可以跟普通的指针一样,使用"*“和”->"等操作
AutoPtr<T>& operator=(const AutoPtr<T>& 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<class T>
AutoPtr<T>& AutoPtr<T>::operator=(const AutoPtr<T>& h)
{
decrUser();
m_pData = h.m_pData;
m_nUser = h.m_nUser;
(*m_nUser)++;
}
5.主函数调用AutoPtr
#include<iostream>
#include <vector>
#include "autoptr.h"
#include "CMatrix.h"
using namespace std;
int main()
{
AutoPtr<CMatrix> h1;
double data[6] = {1,2,3,4,5,6};
h1->Create(2,3,data);
cout << *h1 << endl;
AutoPtr<CMatrix> h2(h1);
(*h2).Set(0,1,10);
cout << *h1 << endl << *h2;
}
6.运行实现截图
可以看到h2是通过拷贝构造函数(拷贝h1)创建的,所以h2调用Set方法后改变的是同一个地址的值,因此h1也跟着改变了。进而导致h1和h2输出结果相同。
|