目录
一、实验内容
二、实验过程
1.模板函数
1.1一般模板函数
1.2特化模板函数
2.类模板Queue
2.1类模板
2.2成员模板函数
2.3 模板特化
3.模板类智能指针
3.1智能指针
3.2构造函数
3.3析构函数
3.4拷贝构造函数
、*等运算符重载 ">3.5等号、->、*等运算符重载
3.6完整代码
总结:
一、实验内容
一、模板函数(compare)
1.一般模板函数
2.特化模板函数
二、类模板Queue
1.类模板(Queue)
2.成员模板函数
3.模板特化:模板函数特化、模板成员函数特化、模板类特化
三、模板类AutoPtr
1.构造函数
2.析构函数
3.拷贝构造函数
4.等号、->、*等运算符重载
5.6主函数调用AutoPtr
二、实验过程
1.模板函数
函数模板是通用的函数描述,它们使用泛型来定义函数,其中的泛型可用具体的类型替换。通过将类型作为参数传递给模板,可使编译器生成该类型的函数。由于模板允许以泛型(而不是具体类型)的方式编写程序,因此有时候也被称为通用编程。
1.1一般模板函数
一个简单的数字比较的代码:
template//使用template 定义Type变量表示Type可以是任何数据类型 int compare(const Type& v1, const Type& v2){ if(v1 v2) return 1; return 0; }
这段代码可以让传入的参数是任意的数据类型,不需要考虑传入的是整形或者字符型等等而单独实现一个函数了。
测试代码:
int main()
{
cout<
运行结果:
1.2特化模板函数
模板特化:就是在实例化模板时,对特定类型的实参进行特殊处理,即实例化一个特殊的实例版本,当以特化定义时的形参使用模板时,将调用特化版本。在我们要比较字符串时,我们就需要特化模板函数。
template <>//声明是模板特化函数
int compare(const char* const& v1, const char * const& v2) //进行特化
{
return strcmp(v1,v2); //调用字符串比较函数
}//模板函数只能写在头文件内,而特化函数的实现只能写在CPP内
测试代码:
int main()
{
cout<
运行结果:
2.类模板Queue
2.1类模板
我们需要编写多个形式和功能都相似的函数,因此有了函数模板来减少重复劳动;我们也需要编写多个形式和功能都相似的类,于是c++引人了类模板的概念,编译器从类模板可以自动生成多个类,避免了程序员的重复劳动。
使用模板类可以使用户为类定义一种模式,使得类中的某些数据成员、某些成员函数的参数、返回值或局部变量能取不同类型(包括系统预定义和用户自定义的)。
C++ 中类模板的写法如下:
template <类型参数表>
class 类模板名{
成员函数和成员变量
};
定义一个类模板Queue和管理类QueueItem
template //Type表示通用数据类型,即可以取任意返回值
class Queue;
template //类模板的定义,类模板的使用实际上是将类模板实例化成一个具体的类
class QueueItem
{
Type item;
QueueItem * next;
QueueItem(const Type& val):item(val),next(NULL){} //参数列表构造器方法
friend Queue; //将队列定义为友元类,方便访问私有变量
friend ostream& operator<<(ostream &os,const Queue &que);
public:
QueueItem* operator++()
{
return next; //返回队列下一个元素的指针
}
Type & operator*() //取出存储的元素
{
return item;
}
};
template
class Queue
{
public:
Queue();
Queue(const Queue &q);
~Queue();
bool isEmpty(); //判断队列是否为空
void push(const Type& val); //在队列里面增加元素
void pop(); //在队列里面删除元素
void destroy(); //清空队列
Type& front(); //输出队列中的第一个元素
void copy_item(const Queue &orig); //拷贝全部队列
template //成员模板函数
Queue(It beg,It end); //新的有参构造函数
template
void assign(It beg,It end); //向队列中添加指定数据
template
void copy_items(It beg,It end); //拷贝部分队列
//访问头部和尾部的函数
const QueueItem* Head() const{return head;}
const QueueItem* End() const{return(tail==NULL)? NULL:tail;}
private:
QueueItem * head; //队列头指针
QueueItem * tail; //队列尾指针,使用含有模板类组成的QueueItem类需要使用模板声明
friend ostream& operator<<(ostream &os,const Queue &que)
{
os<<"< ";
for(QueueItem * p = que.head;p!=NULL;p=p->next)
{
os<item<<" ";
}
os<<">";
return os;
}
};
2.2成员模板函数
//去除队列头部的数据
template void Queue::pop(){
QueueItem * p =head;
head = head->next;
delete p; //释放空间
}
template void Queue::destroy()
{ //删除整个队列数据
while(!empty()){
pop();
}
}
//队列插入数据,是在尾部进行插入
template void Queue::push(const Type& val){
QueueItem * pt = new QueueItem(val);
if(empty()){
head = tail = pt; //头部和尾部指向同一个数据
}
else{
tail->next = pt;
tail=pt; //尾部指针需要始终指向最后一个元素
}
}
template
void Queue::copy_items(const Queue &orig){
for(QueueItem * pt=orig.head;pt;pt=pt->next){
push(pt->item);
}
} //将队列orig的所有元素插入其他队列,原队列元素仍然保留
template
Queue& Queue::operator=(const Queue& q){
destroy();
copy_items(q);
}
template
template void Queue::assign(It beg, It end)
{
destroy();
copy_items(beg, end);//拷贝指定范围的队列区域
}
template
template void Queue::copy_items(It beg, It end)
{
while(beg!=end){
push(beg);
++beg;
} //拷贝指定范围的队列元素插入原队列
}
成员模板函数要写在.h文件里。
测试代码:
//类模板定义对象:类模板名<真实数据类型> 对象名;
Queue xwl;
int c=12;
xwl.push(5);//列表中添加5
xwl.push(c);//列表中添加c
cout< xwl1(a,a+5);
cout< vi(a,a+5);
xwl1.assign(vi.begin()+1,vi.end()-1); //列表中添加4,5,7
cout<
实验结果:
2.3 模板特化
2.3.1模板函数特化
当我们所写的模板无法适应所有数据类型时,就需要对部分函数进行特化,为了能输出想要的字符串结果,对字符串数据进行特化
//针对 char *数据特化push函数
template <>
void Queue::push(const char * const & val){
char* new_item = new char[strlen(val)+1]; //根据字符串长度进行创建字符数组
strncpy(new_item,val,strlen(val)+1); //拷贝字符内容
QueueItem*pt = new QueueItem(new_item); //声明模板特化char *类
if(empty()){
head = tail = pt;
}
else{
tail->next = pt;
tail = pt;
}
}
template <>
void Queue::pop(){
QueueItem *p = head; //特化模板类QueueItem
delete head->item;//char *数据需要自己管理,所以自己释放
head = head->next;
delete p; //释放指针空间
}
2.3.2模板类特化
针对const char*数据类型对类模板Queue实现特化
template <> class Queue
{
private:
void copy_items(const Queue &orig); //拷贝起始元素
QueueItem* head;//队列跟需要头和尾两个指针
QueueItem* tail;//使用含有模板类组成的QueueItem类需要使用模板声明
void destroy(); //释放队列空间
template void copy_items(It beg, It end); //指定范围拷贝队列元素
public:
Queue():head(0),tail(0){}; //参数列表构造器,初始化head指针,tail指针
Queue(const Queue& q):head(0),tail(0){
copy_items(q); //拷贝构造器
}
template
Queue(It beg, It end):head(0),tail(0){copy_items(beg,end);} //指定范围拷贝构造器
template void assign(It beg, It end);
Queue& operator=(const Queue&);
~Queue(){destroy();} //析构函数
const char *& front(){return head->item;} //返回队列最前头的
void push(const char *&val){ //对const char *模板函数进行特化
char* new_item = new char[strlen(val)+1]; //根据字符串长度进行创建字符数组
strncpy(new_item,val,strlen(val)+1); //拷贝字符内容
QueueItem*pt = new QueueItem(new_item); //声明模板特化char *类
if(empty()){
head = tail = pt;
}
else{
tail->next = pt;
tail = pt;
}
}; //将元素放入队列
void pop(){
QueueItem *p = head; //特化模板类QueueItem
delete head->item;//char *数据需要自己管理,所以自己释放
head = head->next;
delete p; //释放指针空间
}; //去除队列头部元素
bool empty() const{return head==0;} //判断队列元素是否为空
friend ostream& operator<<(ostream& os, const Queue &q)
{
os<<"< ";
QueueItem * p;
for(p=q.head;p;p=p->next)
{
os<item<<" ";
}
os<<">";
return os;
}
//访问头部和尾部的函数
const QueueItem* Head() const{return head;}
const QueueItem* End() const {return(tail==NULL)?NULL:tail;}
};
测试代码:
Queue xwl;
xwl.push("hi");
xwl.push("I'm");
xwl.push("wenlong xie");
cout<
运行结果:
3.模板类智能指针
3.1智能指针
智能指针是行为类似于指针的类对象,所有的智能指针都会重载 -> 和 * 操作符。智能指针是存储指向动态分配(堆)对象指针的类,用于生存期控制,能够确保自动正确的销毁动态分配的对象,防止内存泄露。对一个对象进行初始化时不能将一个普通指针直接赋值给智能指针,因为一个是指针,一个是类。可以通过构造函数传入普通指针。
3.2构造函数
构造函数就像普通成员函数一般使用模板
//构造函数,构建使用智能指针的某个实例
template
AutoPtr::AutoPtr(T* pData)
{
ptr = pData;
user = new int(1); //new出一个int对象,并且初始化为1;user = new int[1];new出一个数组
}
3.3析构函数
一般来说我们不将一堆代码写在析构函数里,而是通过析构函数调用其他函数,这里我们在智能指针释放时同时释放里面的数据
template
AutoPtr::~AutoPtr()
{
decrUser();
}
//用户数减少时应该判断是否释放变量内存(=0时)
template
void AutoPtr::decrUser()
{
(*user)--;
if((*user)==0)
{
delete ptr;
ptr = 0;
delete user;
user = 0;
}
}
3.4拷贝构造函数
//智能指针赋初值为另一个智能指针指向的变量
template
AutoPtr::AutoPtr(const AutoPtr& handle)
{
ptr = handle.ptr;
user = handle.user;
(*user)++; //变量的用户数+1
}
3.5等号、->、*等运算符重载
//类中
T* operator->() //重载赋值运算符"->",返回ptr指针
{
return ptr;
}
const T* operator->() const
{
return ptr;
}
T operator*() const //重载赋值运算符"*",取出指针所指向地址中的内容
{
return *ptr;
}
//类外部
template
AutoPtr& AutoPtr::operator=(const AutoPtr& handle) //重载赋值运算符
{
//自己引用自己则用户数不变
if(this==&handle)
return *this;
//在自身指向别人之前,自身指向的变量用户数减1
decrUser();
ptr = handle.ptr;
user = handle.user;
(*user)++;
return *this;
}
3.6完整代码
template
class AutoPtr
{
public:
AutoPtr():ptr(0),user(0){};
AutoPtr(T* pData); //构造函数
AutoPtr(const AutoPtr& handle);
~AutoPtr(); //析构函数
AutoPtr& operator=(const AutoPtr& handle); //重载赋值运算符"="
void decrUser(); //减少用户数
T* operator->() //重载赋值运算符"->",返回ptr指针
{
return ptr;
}
const T* operator->() const
{
return ptr;
}
T operator*() const //重载赋值运算符"*",取出指针所指向地址中的内容
{
return *ptr;
}
T* get() const {return ptr;} //返回保存的指针
T getUser() const {return *user;}; //返回保存的用户数
private:
T* ptr = 0; //指向数据的指针
int* user=0; //指向用户数指针,用*是因为需要统一修改
};
//构造函数,构建使用智能指针的某个实例
template
AutoPtr::AutoPtr(T* pData)
{
ptr = pData;
user = new int(1); //new出一个int对象,并且初始化为1;user = new int[1];new出一个数组
}
//智能指针赋初值为另一个智能指针指向的变量
template
AutoPtr::AutoPtr(const AutoPtr& handle)
{
ptr = handle.ptr;
user = handle.user;
(*user)++; //变量的用户数+1
}
template
AutoPtr::~AutoPtr()
{
decrUser();
}
template
AutoPtr& AutoPtr::operator=(const AutoPtr& handle) //重载赋值运算符
{
//自己引用自己则用户数不变
if(this==&handle)
return *this;
//在自身指向别人之前,自身指向的变量用户数减1
decrUser();
ptr = handle.ptr;
user = handle.user;
(*user)++;
return *this;
}
//用户数减少时应该判断是否释放变量内存(=0时)
template
void AutoPtr::decrUser()
{
(*user)--;
if((*user)==0)
{
delete ptr;
ptr = 0;
delete user;
user = 0;
}
}
测试代码:
int s1 = 15;
int s2 = s1;
AutoPtr x1(new int(5));
AutoPtr x2(new int(s1));
AutoPtr x3(new int(s2));
if(x1.get()==0)
{
cout<<"x1 is NULL"<data:"<<*x3<
运行结果:
x1开始指向的数据是 5,地址为0x711770,x2指向的数据是15,地址是0x711ae0,x3指向的是s2,令s2 = s1=15,地址是0x711b20,然后执行x1=x2,让x1指向x2,地址变为0x711ae0,接着执行x3=x1,x3地址变为0x711ae0,与x1指向同一个数据s1=15,此时x1,x2,x3都指向s1,用户数为3,生命周期结束,执行析构函数,释放三个指针。
总结:
1.类模板及其成员函数的实现写在.h文件里,特化的模板写在.cpp文件里。
2.智能指针在函数结束时自动释放内存空间,不需要手动释放内存空间,在内存泄漏方面起到很好的作用。



