实验背景
本次实验基于Qt Creator下的c++环境,实现了关于C++特有的模板功能,C++最重要的特性之一就是代码重用,为了实现代码的通用性,需要让代码不受数据类型的影响,并且可以自动适应数据类型的变化。这就是参数化程序设计,模板是参数化程序设计的工具之一,可以实现参数多态性。所谓参数多态性,就是将程序所处理的对象的类型参数化,使得一段程序可以用于处理多种不同类型的对象。
1.模板函数
模板函数定义形式: template<模板参数表> 类型名 函数名(参数表) { 函数体的定义 } 所有函数模板的定义都是用关键字template开始的。函数参数表由用逗号分隔的模板参数构成,可以包括以下内容 1.class(或typename)标识符,指明可以接受一个类型参数。这些类型参数代表的是类型,可以是预定义类型或者自定义类型。 2.类型说明符 标识符,指明可以接受一个由"类型说明符"所规定类型的常量作为参数。 3.template<参数表>class 标识符,指明可以接收一个类模板名作为参数。 看起来有点蒙,我们举个具体例子
1.1.一般模板函数
如果我们要比较数据的大小,我们一般会使用compare函数,而如果我们要比较int和char两种数据类型,我们就需要定义两种输入参数的函数。
int compare(const int v1,const int v2)
{
if(v1>v2)
return 1;
else if(v1<v2)
return -1;
else
return 0;
}
int compare(const char v1,const char v2)
{
if(v1>v2)
return 1;
else if(v1<v2)
return -1;
else
return 0;
}
#endif
主函数:
int main()
{
int a=20,b=10;
char m='m',x='x';
cout<<compare(a,b)<<endl;
cout<<compare(m,x)<<endl;
return 0;
}
通过函数重载来进行不同数据类型的比较,但如果需要比较的数据类型增多,这样会很麻烦,模板函数只需要一个函数即可解决所有的函数类型。 模板函数:
template <typename T>
int compare(const T v1,const T v2)
{
if(v1>v2)
return 1;
else if(v1<v2)
return -1;
else
return 0;
}
这样不管是什么类型的数据,都可以用这个函数进行比较。 总结: 1.函数模板中的类型参数T,表示一种抽象的类型,T没有任何含义,只是一个代号。当编译器检测到调用compare函数时,会用第一个实参的类型替换掉整个模板定义中的T,重新构造成一个完整的函数后编译这个新建的函数。 2.函数模板和重载时密切相关的。从函数模板产生的相关函数都是同名的,编译器用重载的解决的方法调用相应的函数。
1.2.特化模板函数
尽管模板有这么多好处,但我们要明确的认识到,世界上不可能存在一种方法可以解决所有的问题。所以就要引入特化模板,专门针对某种类型做出调整。
template <>
int compare<char*>(char*v1,char*v2);
template<>
int compare<char*>(char*v1,char*v2)
{
return strcmp(v1,v2);
}
这样就实现了关于语句的比较,而特化声明要放在模板函数的下面。 函数模板注意事项: 1.函数模板本身在编译时不会生成任何目标代码,只有由模板生成的实例会生成目标代码。 2.被多个源文件引用的函数模板,应当连同函数体一同放在头文件中,而不能像普通函数那样只将声明放在头文件中。 3.函数指针也只能指向模板的实例,而不能指向模板本身。
2.模板类Queue
类模板声明的语法形式是: template<模板参数表> class 类名 { 类成员声明 } 典型的例子就是vector,这个容器就是一个类模板,可以存入或读取任意类型的变量。这就是我们使用类模板的目的,可以让任意类型的对象使用相同的方法,而不需要单独设置。
2.1.模板类
template <class Type>
class QueueItem
{
Type item;
QueueItem * next;
QueueItem(const Type & data):item(data),next(0){};
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
{
private:
void copy_items(const Queue &orig);
QueueItem<Type>* head;
QueueItem<Type>* tail;
void destroy();
template<class It> void copy_items(It beg, It end);
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;}
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;}
};
2.2.成员模板函数
和模板函数一样,成员模板函数提供了任意类型均可使用的方法
template <class Type> void Queue<Type>::pop(){
QueueItem<Type> * p =head;
head = head->next;
delete p;
}
template <class Type> void Queue<Type>::destroy()
{
while(!empty()){
pop();
}
}
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<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;
}
}
2.3.模板特化
模板成员函数特化和模板函数特化一样,头文件声明,cpp文件实现,特化类型在<>内声明
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 Queue<const char *>
{
private:
void copy_items(const Queue &orig);
QueueItem<const char *>* head;
QueueItem<const char *>* tail;
void destroy();
template<class It> void copy_items(It beg, It end);
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();}
const char *& front(){return head->item;}
void push(const char *&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;
}
};
void pop(){
QueueItem<const char *> *p = head;
delete head->item;
head = head->next;
delete p;
};
bool empty() const{return head==0;}
friend ostream& operator<<(ostream& os, const Queue<const char *> &q)
{
os<<"< ";
QueueItem<const char *> * p;
for(p=q.head;p;p=p->next)
{
os<<p->item<<" ";
}
os<<">";
return os;
}
const QueueItem<const char *>* Head() const{return head;}
const QueueItem<const char *>* End() const {return(tail==NULL)?NULL:tail;}
};
3.模板类AutoPtr
对于C和C++来说,指针的使用即是特色,也是弱点,不合适的指针使用可能会导致一系列问题,比如内存溢出,数据泄露等。因此如何更方便的使用指针呢,这就是AutoPtr(智能指针)的目标。
3.1.构造函数
template<class T>
AutoPtr<T>::AutoPtr(T* pData)
{
m_pData = pData;
m_nUser = new int(1);
}
3.2.析构函数
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.3.拷贝构造函数
template<class T>
AutoPtr<T>::AutoPtr(const AutoPtr<T>& h)
{
m_pData = h.m_pData;
m_nUser = h.m_nUser;
(*m_nUser)++;
}
3.4.等号、->、*等运算符重载
=重载
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;
}
3.5.主函数调用AutoPtr
void TestAutoPtr();
int main()
{
TestAutoPtr();
return 0;
}
void TestAutoPtr()
{
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通过拷贝构造函数创建,h2调用Set方法后改变的是同一个地址的值,所以最后h1和h2输出结果相同。
|