目录
实验内容:
一、模板函数(compare):
1. 一般模板函数:
2.特化模板函数:
二、模板类Queue:
1.模板类(Queue):
2. 成员模板函数:
3.模板特化:模板函数特化、模板成员函数特化、模板类特化:
3.1.模板函数特化:
3.2.模板类特化:
三、模板类AutoPtr:
1.构造函数:
2.析构函数:
3.拷贝构造函数:
、*等运算符重载:">4.等号、->、*等运算符重载:
5.主函数调用AutoPtr:
总结:
实验内容:
1、模板函数(compare)
一般模板函数
特化模板函数
2、模板类Queue或Stack
模板类(Queue,Stack)
成员模板函数
模板特化:模板函数特化、模板成员函数特化、模板类特化
3、模板类AutoPtr
构造函数
析构函数
拷贝构造函数
等号、->、*等运算符重载
主函数调用AutoPtr
一、模板函数(compare):
数据的值可以通过函数参数传递,在函数定义时数据的值是未知的,只有等到函数调用时接收了实参才能确定其值。 在C++中,数据的类型也可以通过参数来传递,在函数定义时可以不指明具体的数据类型,当发生函数调用时,编译器可以根据传入的实参自动推断数据类型。
所谓函数模板,实际上是建立一个通用函数,它所用到的数据的类型(包括返回值类型、形参类型、局部变量类型)可以不具体指定,而是用一个虚拟的类型来代替(实际上是用一个标识符来占位),等发生函数调用时再根据传入的实参来逆推出真正的类型。这个通用函数就称为函数模板。
1. 一般模板函数:
//当v1v2时返回1
//一般模板函数,使用template 定义Type变量表示Type可以是任何数据类型,既能比较浮点数的大小又能比较整型数的大小
template
int compare(const Type& v1, const Type& v2)
{
if(v1v2) return 1 ;
else return 0;
}
所有函数模板的定义都是用关键字 template 开始的,该关键字之后是使用尖括号 <> 括起来的类型参数表。每一个类型参数 T 之前都有关键字 class 或者关键字 typename,这些类型参数代表的是类型,可以是内部类型或自定义类型。这样,类型参数就可以用来指定函数模板本身的参数类型和返回值类型,以及声明函数中的局部变量。函数模板中函数体的定义方式与定义其它函数类似。
2.特化模板函数:
对于该函数模板,当实参为两个char指针时,比较的是指针的大小,而不是指针指向内容的大小,此时就需要为该函数模板定义一个特化版本,即特殊处理的版本,可以对字符串进行特殊处理:
template <>//声明是模板特化函数 int compare(const char* const& v1, const char * const& v2) //进行全特化 { return strcmp(v1,v2); //调用字符串比较函数 }
模板函数只能写在头文件内,而特化函数的实现只能写在CPP内
二、模板类Queue:
1.模板类(Queue):
类模板声明的语法形式:
template<模板参数表>
class类名{
//类成员声明
}
使用类模板,可以为类声明一种模式,使得类中的某些数据成员、某些成员函数的参数、某些成员函数的返回值能取任意类型(包括系统预定义的和用户预定义的)。
template//Type表示通用数据类型,即可以取任意返回值 class Queue; template //为类取一种模式使得Type变量可以取任意返回值 class QueueItem //对Queue队列数据的管理 { 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. 成员模板函数:
如果需要在类模板以外定义其成员函数,则需要采用以下形式:
template<模板参数表> 类型名类名::函数名(参数表)
成员模板函数写在.h文件里:
//去除队列头部的数据 templatevoid 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; } //拷贝指定范围的队列元素插入原队列 }
测试相关函数:
3.模板特化:模板函数特化、模板成员函数特化、模板类特化:
3.1.模板函数特化:
当我们所写的模板函数无法对所有数据类型生效的时候,我们就需要对部分函数进行特化:
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; //释放指针空间 }
3.2.模板类特化:
当类模板内需要对某些类型进行特别处理时,使用类模板的特化:
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; //释放指针空间
}; //去除队列头部元素
测试相关函数:
三、模板类AutoPtr:
C++提供了智能指针,会自动判断指针当前是否应该释放空间,从而由系统代替用户管理空间内存,AutoPtr定义了类似指针的对象,将 new 获得的地址赋给该对象。当AutoPtr对象过期时,析构函数将使用 delete 来释放内存。如果将 new 返回的地址赋值给 AutoPtr对象,无须记住还需要释放这些内存。在 AutoPtr 对象过期时,内存将自动被释放。
1.构造函数:
构造函数和普通成员函数相似使用模板:
//构造函数,构建使用智能指针的某个实例 templateAutoPtr ::AutoPtr(T* pData) { m_pData = pData; m_nUser = new int(1);//new出一个int型,并初始化为1 //user = new int[1];new出一个数组 }
2.析构函数:
当对象结束生命周期,如对象所在的函数已调用完毕时,系统会自动执行析构函数。这里是在智能指针释放时同时释放里面的数据
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(){ decrUser();
3.拷贝构造函数:
拷贝构造函数是构造函数的一种,它只有一个参数,参数类型是本类的引用,它由编译器调用来完成一些基于同一类的其他对象的构建及初始化。拷贝构造函数的参数可以是 const 引用,也可以是非 const 引用。 一般使用前者,这样既能以常量对象(初始化后值不能改变的对象)作为参数,也能以非常量对象作为参数去初始化其他对象。如果类的设计者不写拷贝构造函数,编译器就会自动生成拷贝构造函数。
//智能指针赋初值为另一个智能指针指向的变量 templateAutoPtr ::AutoPtr(const AutoPtr & h) //创建名为新的Autoptr对象,新的智能指针保存原来存在的指针。 将所有权转给新指针,原来的智能指针成为未绑定的Auto_ptr对象, 会在生命周期结束后自动释放 { m_pData = h.m_pData; m_nUser = h.m_nUser; (*m_nUser)++; //变量的用户数+1 }
4.等号、->、*等运算符重载:
运算符重载,就是对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型。
T* operator->(){//运算符->重载,返回m_pData指针
return m_pData;
}
T& operator*(){//运算符*号重载,取出指针指向地址中的内容
return *m_pData;
}
5.主函数调用AutoPtr:
int s1 = 20;
AutoPtr t1(new int(10));
AutoPtr t2(new int(s1));
cout<<"t1: "< t3(new int(s2));
cout<<"t3: "<data:"<<*t3<
运行结果:
通过运行结果可以清晰看出地址之间的变化,release是析构函数运行的输出结果。
智能指针全部代码:
#ifndef AUTOPTR_H
#define AUTOPTR_H
using namespace std;
#include
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;
cout<<"release"<
总结:
通过本次实验,我初步了解了C++一般模板函数和特化模板函数的应用,以及使用了模板类Queue和特化模板类构造了队列数据结构,学会了智能指针的基本性质和并构造运行了智能指针。
运行结果:
通过运行结果可以清晰看出地址之间的变化,release是析构函数运行的输出结果。
智能指针全部代码:
#ifndef AUTOPTR_H #define AUTOPTR_H using namespace std; #includetemplate 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; cout<<"release"< 总结:
通过本次实验,我初步了解了C++一般模板函数和特化模板函数的应用,以及使用了模板类Queue和特化模板类构造了队列数据结构,学会了智能指针的基本性质和并构造运行了智能指针。



