模板
一、模板函数(compare)
1. 一般模板函数
首先写一个判断是否相等的模板函数,注意声明和函数体都写在头文件中。
//模板函数需要将声明和函数体都放在头文件中
//一般模板函数
template<class T>
bool compare(const T& a, const T& b)
{
return a == b;
}
2. 特化模板函数
使用模板时会遇到一些特殊的类型需要特殊处理,不能直接使用当前的模板函数,所以此时我们就需要对该类型特化出一个模板函数。
此处,该函数无法针对字符串类型使用,则需特化模板函数。
//上述模板函数对于字符串比较无法使用,所以需要特化
//格式:template<>不用写类型
// 函数名<特化类型>(特化类型 参数1, 特化类型 参数2,...)
//注意声明必须放在头文件中,实例化必须在cpp文件中
template<>
bool compare<const char*>(const char* const& a, const char* const& b);
template<>
bool compare<const char*>(const char* const& a, const char* const& b)
{
return strcmp(a, b) == 0;
}
3. 测试
#include<iostream>
#include"queue.h"
using namespace std;
int main()
{
const int a = 1;
const int b = 1;
const char* c = "qwe", * d = "asd";
cout << compare(a, b) << endl;
cout << compare(c, d);
}
运行结果:
4. 小结
一般特化函数格式
template< 模板参数表>
类型名 参数名(参数表)
{
函数体的定义
}
特化模板函数注意点
- 使用模板特化时,必须要先有基础的模板函数(就是上面第一个模板函数)
- 使用特换模板函数时格式有要求:
2.1. template 后直接跟<> 里面不用写类型
2.2. 函数名<特化类型>(特化类型 参数1, 特化类型 参数2 , …) 在函数名后跟<>其中写要特化的类型- 特化的函数的函数名,参数列表要和原基础的模板函数想相同,避免不必要的错误
- 声明必须放在头文件中,实例化必须在cpp文件中
函数模板与函数的区别
- 函数模板本身在编译时不会生成任何目标代码,只有由模板生成的实例会生成目标代码。
- 被多个源文件引用的函数模板,应当连同函数体一同放在头文件中,而不能像普通函数那样只将声明放在头文件中。
- 函数指针也只能指向模板的实例,而不能指向模板本身。
二、模板类Queue或Stack
1. 模板类(Queue,Stack)
类模板格式:
template<模板参数表>
class 类名
{
类成员声明
};
如类模板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;
}
};
模板参数表中参数可以声明为该模板类的友元类
如以上代码第6,7行的友元类
可通过typedef或者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. 模板特化:模板函数特化、模板成员函数特化、模板类特化
3.1. 模板函数特化
模板函数特化见目录一、模板函数 2.特化模板函数
3.2. 模板成员函数特化
头文件中声明
template<>
void Queue<const char*>::push(const char * const &val) ;
template<>
void Queue<const char*>::pop();
cpp文件中实现
template<>
void Queue<const char*>::push(const char * const &val)
{
char* new_item = new char[str1en(va1)+1];
strncpy(new_item, val, strlen(va1)+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.3. 模板类特化
//普通类模板
template<模板参数表>
class 类名
{
类成员声明
};
//模板类特化
template<> //<>里为空
class 类名<特化类型> //<>里指明具体类型
{
类成员声明
};
4. 代码:
queue.h
#ifndef QUEUE_H
#define QUEUE_H
#include <iostream>
using namespace std;
//模板函数需要将声明和函数体都放在头文件中
//一般模板函数
template<class T>
bool compare(const T& a, const T& b)
{
return a == b;
}
//上述模板函数对于字符串比较无法使用,所以需要特化
//格式:template<>不用写类型
// 函数名<特化类型>(特化类型 参数1, 特化类型 参数2,...)
//注意声明必须放在头文件中,实例化必须在cpp文件中
template<>
bool compare<const char*>(const char* const& a, const char* const& b);
template<class Type> class Queue;
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);
};
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;
}
}
template<>
void Queue<const char*>::push(const char * const &val) ;
template<>
void Queue<const char*>::pop();
template<class Type>
void Queue<Type>::copy_items(const Queue &orig)
{
for(QueueItem<Type> *pt = orig.head;pt;pt=pt->next)
{
push(pt->item);
}
}
template<class Type>
Queue<Type>& Queue<Type>::operator=(const Queue& q)
{
destroy();
copy_items(q);
}
template<class Type> template<class It>
void Queue<Type>::assign(It beg,It end)
{
destroy();
copy_items(beg,end);
}
template<class Type> template<class It>
void Queue<Type>::copy_items(It beg,It end)
{
while(beg!=end)
{
push(*beg);
++beg;
}
}
#endif // QUEUE_H
queue.cpp
#include "queue.h"
#include <string.h>
template<>
bool compare<const char*>(const char * const &a, const char * const &b)
{
return strcmp(a,b) == 0;
}
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;
}
Test.cpp
#include<iostream>
#include"queue.h"
#include <vector>
#include "autoptr.h"
#include "CMatrix.h"
using namespace std;
int main()
{
//const int a = 1;
//const int b = 1;
//const char* c = "qwe", * d = "asd";
//cout << compare(a, b) << endl;
//cout << compare(c, d);
Queue<int> qt;
double d = 3.3;
qt.push(1);
qt.push(d);
qt.push(10);
cout << endl;
cout << qt;
short a[5] = { 0,3,6,9 };
Queue<int> qi(a, a + 5);
cout << endl;
cout << qi;
cout << endl;
while (!qi.empty())
{
cout << qi.front() << " ";
qi.pop();
}
vector<int> vi(a, a + 5);
qi.assign(vi.begin(), vi.end());
cout << endl;
cout << qi;
cout << endl;
Queue<const char*> q1;
q1.push("hi");
q1.push("I'm");
q1.push("Jerry");
cout << q1;
cout << endl;
Queue<const char*> q2(q1);
cout << q2;
//AutoPtr<CMatrix> h1;
//double data[6] = {1,2,3,4,5,6};
//h1->Create(2,3,data);
//AutoPtr<CMatrix> h2(h1);
//(*h2).Set(0,1,10);
//cout<<*h1<<*h2;
}
运行结果:
三、模板类AutoPtr
1. 构造函数
函数声明
AutoPtr(T* pData);
函数实例化
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. 拷贝构造函数
函数声明
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;
(*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"queue.h"
#include <vector>
#include "autoptr.h"
#include "CMatrix.h"
using namespace std;
int main()
{
//const int a = 1;
//const int b = 1;
//const char* c = "qwe", * d = "asd";
//cout << compare(a, b) << endl;
//cout << compare(c, d);
//Queue<int> qt;
//double d = 3.3;
//qt.push(1);
//qt.push(d);
//qt.push(10);
//cout << endl;
//cout << qt;
//short a[5] = { 0,3,6,9 };
//Queue<int> qi(a, a + 5);
//cout << endl;
//cout << qi;
//cout << endl;
//while (!qi.empty())
//{
// cout << qi.front() << " ";
// qi.pop();
//}
//vector<int> vi(a, a + 5);
//qi.assign(vi.begin(), vi.end());
//cout << endl;
//cout << qi;
//cout << endl;
//Queue<const char*> q1;
//q1.push("hi");
//q1.push("I'm");
//q1.push("Jerry");
//cout << q1;
//cout << endl;
//Queue<const char*> q2(q1);
//cout << q2;
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;
}
运行结果如下:h2通过拷贝构造函数创建(拷贝h1),则h2调用Set方法后改变的是同一个地址的值,所以最后h1改变了,h1和h2输出结果相同。
6. 总结
在C++中,动态内存的管理是用一对运算符完成的:new和delete,new:在动态内存中为对象分配一块空间并返回一个指向该对象的指针,delete:指向一个动态独享的指针,销毁对象,并释放与之关联的内存。
动态内存管理经常会出现两种问题:一种是忘记释放内存,会造成内存泄漏;一种是尚有指针引用内存的情况下就释放了它,就会产生引用非法内存的指针。
为了更加容易(更加安全)的使用动态内存,引入了智能指针的概念。智能指针的行为类似常规指针,重要的区别是它负责自动释放所指向的对象。