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

C++ 模板类与智能指针

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

C++ 模板类与智能指针

目录

一、实验内容

二、实验过程

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(v1v2) 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.智能指针在函数结束时自动释放内存空间,不需要手动释放内存空间,在内存泄漏方面起到很好的作用。

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

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

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