栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C++ 实验3 模板

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C++ 实验3 模板

目录

实验内容:

一、模板函数(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文件里:

//去除队列头部的数据
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;
    } //拷贝指定范围的队列元素插入原队列
}

 测试相关函数:

 

 

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.构造函数:

        构造函数和普通成员函数相似使用模板:

//构造函数,构建使用智能指针的某个实例 
template
 AutoPtr::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 引用。 一般使用前者,这样既能以常量对象(初始化后值不能改变的对象)作为参数,也能以非常量对象作为参数去初始化其他对象。如果类的设计者不写拷贝构造函数,编译器就会自动生成拷贝构造函数。

//智能指针赋初值为另一个智能指针指向的变量
template
 AutoPtr::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和特化模板类构造了队列数据结构,学会了智能指针的基本性质和并构造运行了智能指针。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/529512.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号