IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++实验三——模板 -> 正文阅读

[C++知识库]C++实验三——模板

目录

1. 基础概念

2. 模板函数

? ? ? ? 2.1 一般模板函数

? ? ? ? 2.2 特化模板函数

3. 类模板

????????3.2 成员模板函数

????????3.3 类模板特化

4.?模板类AutoPtr

????????4.1 构造函数

? ? ? ? 4.2 析构函数

????????4.3 运算符重载

5. 小结


1. 基础概念

? ? ? ? 模板定义:模板是泛型编程的基础,泛型编程即以一种独立于任何特定类型的方式编写代码。?模板是创建泛型类或函数的蓝图或公式。库容器,比如迭代器和算法,都是泛型编程的例子,它们都使用了模板的概念。

????????模板分类:函数模板和类模板。函数模板针对参数类型不同的函数;类模板仅针对数据成员和成员函数类型不同的类。

????????使用模板目的:让程序员编写与类型无关的代码。

????????注意:模板的声明或定义只能在全局,命名空间或类范围内进行。即不能在局部范围,函数内进行,如不能在main函数中声明或定义一个模板。

2. 模板函数

? ? ? ? 2.1 一般模板函数

????????函数模板是通用的函数描述,它们使用泛型来定义函数,其中的泛型可用具体的类型替换。通过将类型作为参数传递给模板,可使编译器生成该类型的函数。由于模板允许以泛型(而不是具体类型)的方式编写程序,因此有时候也被称为通用编程。下面是一段一般模板函数:

template <class T> int compare(const T &v1, const T &v2)
{
 if(v1 < v2) return -1;
 if(v2 > v1) return 1;
 return 0;
}

? ? ? ? 2.2 特化模板函数

????????所谓特化,就是将泛型的变得具体化。从字面上来解释,就是为已有的模板参数进行一些使其特殊化的指定,使得以前不受任何约束的模板参数,或受到特定的修饰(例如const变为了指针,甚至是经过别的模板类包装之后的模板类型)或完全被指定了下来。

????????对于上面函数模板,当实参为两个char指针时,比较的是指针的大小,而不是指针指向内容的大小,此时就需要为该函数模板定义一个特化版本,即特殊处理的版本:

//为实参类型 const char * 提供特化版本
template <> int compare<const char *>(const char * const &v1, const char * const &v2)
{
 return strcmp(v1, v2);
}

????????compare<const char *> //模板名字后指定特化时的模板形参即const char *类型,就是说在以实参类型 const char * 调用函数时,将产生该模板的特化版本,而不是泛型版本,也可以为其他指针类型定义特化版本如int *。

3. 类模板

? ? ? ? 3.1 类模板(queue)

????????所谓类模板,实际上是建立一个通用类,其数据成员、成员函数的返回值类型和形参类型不具体指定,用一个虚拟的类型来代表。使用类模板定义对象时,系统会实参的类型来取代类模板中虚拟类型从而实现了不同类的功能。定义一个类模板与定义函数模板的格式类似,必须以关键字template开始,后面是尖括号括起来的模板参数,然后是类名。

? ? ? ? 下面给出一个类模板:

queue.h

#ifndef QUEUE_H
#define QUEUE_H
#include <iostream>
using namespace std;
template<class Type> class Queue;
template<class Type> class QueueItem{
    QueueItem(const Type &t):item(t), next(0){}
    Type item;
    QueueItem * next;
    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{
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;}
    const Type& front() const{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‐>next;}
private:
    QueueItem<Type> * head;
    QueueItem<Type> * tail;
    void destroy();
    void copy_items(const Queue &);
    template<class It> void copy_items(It beg,It end);
};

template<class Type>void Queue<Type>::destroy()
{
    while (!empty()) {
        pop();
    }
}

template<class Type> void Queue<Type>::pop(){
    QueueItem<Type> * p = head;
    head = head‐>next;
    delete p;
}
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<>
void Queue<const char*>::push(const char * const &val);
template<>
void Queue<const char*>::pop();


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;
    }
}

//template<> class Queue<const char*>{
//public:
//    void push(const char* str){real_queue.push(str);}
//    void pop(){real_queue.pop();}
//    bool empty() const {return real_queue.empty();}
//    string front() {return real_queue.front();}
//    const string &front() const{return real_queue.front();}
//private:
//    Queue<string> real_queue;
//};

template<class Type>
int compare(const Type& v1, const Type& v2)
{
   if(v1<v2) return ‐1;
   if(v1>v2) return 1
   return 0;
}
template<>
int compare<const char*>(const char * const &v1, const char * const &v2);


#endif // QUEUE_H

queue.cpp

#include "queue.h"
#include <iostream>
#include <vector>
#include <string.h>
using namespace std;
//template<>
//class Queue<const char*>{
//public:
//    void Push(const char* str){real_queue.Push(str);}
//    void Pop(){real_queue.Pop();}
//    bool isEmpty()  {return real_queue.isEmpty();}
//    string front() const {return real_queue.front();}
//    friend ostream & operator<<(ostream& os, Queue<const char*> &que){
//        os<<que.real_queue;
//    }
// //    const string &front() const{return real_queue.front();}

//private:
//    Queue<string> real_queue;

//};
template <>
void Queue<const char*>::Push( const char * const & str)
{
    char * pVal = new char[strlen(str)+1];
    strcpy(pVal,str);
    QueItem<const char*> * p = new QueItem<const char*>(pVal);
    if(isEmpty())
    {
        head = tail = p;
    }
    else {
        tail‐>next = p;
        tail = p;
    }
}
template<>
void Queue<const char* >::Pop()
{
    if(isEmpty())
    {
        return;
    }
    QueItem<const char*> * p = head;
    head = head‐>next;
    delete []p‐>item;
    delete p;
}
template <>
int compare(const char* const a, const char* const b)
{
    return  strcmp(a,b);
}

void testTemplate()
{
    double a = 1.2;
    double b = 1.5;
    cout<<compare(a,b);

    Queue<int> qt;
    double d = 3.3;
    qt.Push(1);
    qt.Push(d);
    qt.Push(10);
    cout<<qt;

    short data[5] = {0,3,6,9};
    Queue<int> qi(data,data+5);
    cout<<endl;
    cout<<qi;
    while(!qi.isEmpty())
    {
        cout<<qi.front()<<" ";
        qi.Pop();
    }

    vector<int> vi(data,data+5);
    qi.assign(vi.begin(),vi.end());
    cout<<endl;
    cout<<qi;

    Queue<const char*> qst;
    char str[10];
    strcpy(str,"I am");
    qst.Push(str);
    strcpy(str,"Zongyue");
    qst.Push(str);
    strcpy(str,"Wang");
    qst.Push(str);
    cout<<endl<<qst;

    char str1[10];
    strcpy(str,"abc");
    strcpy(str1,"abc");
    cout<<"1 2 "<<compare<const char*>(str,str1)<<endl;
    cout<<"2 1 "<<compare<const char*>(str1,str)<<endl;

}

????????3.2 成员模板函数

????????成员模板函数特化:

#include<stdio.h>
template <class T>
class Sample
{
public:
    void print() {printf("\nprint template");}
};

void Sample<int>::print() {printf("\nprint int");};

????????3.3 类模板特化

????????你可以用模板实参来特化类模板,和函数模板的重载类似,通过特化类模板,你可以优化基于某种特定类型的实现,或者克服某种特定类型在实例化类模板时所出现的不足,另外,如果要特化一个类模板,你还要特化该类模板的所有成员函数,虽然也可以只特化某个成员函数,但这个做法并没有特化整个类,也就没有特化整个模板。

为了特化一个类模板,你必须在起始处声明一个template<>,接下来声明用来特化类模板的类型,这个类型被用作模板实参,且必须在类名的后面直接指定。

? ? ? ? 类模板特化:

#include<stdio.h>

class Sample2
{
public:
    template <class T>
    void print()
    {printf("\nSample2 print template");}
};

template <>
void Sample2::print<int>()
{printf("\nSample2 print int");}

// 调用
int _tmain(int argc, _TCHAR* argv[])
{
    Sample2 sam2;
    sam2.print<double>();
    sam2.print<int>();
    return 0;
}

4.?模板类AutoPtr

????????auto_ptr是C++标准库中(<utility>)为了解决资源泄漏的问题提供的一个智能指针类模板(注意:这只是一种简单的智能指针)auto_ptr的实现原理其实就是RAII(Resource Application Immediately Initialize),在构造的时候获取资源,在析构的时候释放资源,并进行相关指针操作的重载,使用起来就像普通的指针。使用auto_ptr作为成员变量,以避免资源泄漏。为了防止资源泄漏,我们通常在构造函数中申请,析构函数中释放,但是只有构造函数调用成功,析构函数才会被调用,换句话说,如果在构造函数中产生了异常,那么析构函数将不会调用,这样就会造成资源泄漏的隐患。比如,如果该类有2个成员变量,指向两个资源,在构造函数中申请资源A成功,但申请资源B失败,则构造函数失败,那么析构函数不会被调用,那么资源A则泄漏。为了解决这个问题,我们可以利用auto_ptr取代普通指针作为成员变量,这样首先调用成功的成员变量的构造函数肯定会调用其析构函数,那么就可以避免资源泄漏问题。

????????4.1 构造函数

????????构造函数1:explicit auto_ptr(Type* _Ptr = 0) throw( );

    auto_ptr<int> pt;//包含一个int*的指针,并初始化为NULL
    auto_ptr<int> pt(new int(123)); //包含一个int*的指针,并初始化为123的地址
    auto_ptr<int> pt = new int(123); //error!构造函数声明为explicit

????????构造函数2:auto_ptr(auto_ptr<Type>& _Right) throw( );

     int* ptr = new int();

     auto_ptr<int> pt1(ptr); //构造函数1

     auto_ptr<int> pt2(pt1); //将pt1的使用权转给pt2,注意pt1指向NULL了
                             //pt1调用了本身的release()函数,将内部指针地址传给pt2

????????构造函数3:template<typename Other>

auto_ptr(auto_ptr<Other>& _Right) throw( );

????????声明这样一个拷贝构造函数的目的,就是为了派生类指针能转换成基类的指针。例:

    class Base { };

    class Derived : public Base { };

    auto_ptr<Derived> pDerived(new Derived);

    auto_ptr<Base>    pBase(pDerived);     //让这样的代码能通过编译器其本质是为了让,auto_ptr类内部的Derived*转换为Base* 

? ? ? ?4.2 析构函数

? ? ? ? 析构函数:

    ~autoptr()//析构函数
    {
        if(_ptr!=NULL)
        {
            delete _ptr;
        }
    }

????????4.3 运算符重载

? ? ? ? 等号运算符重载:

AutoPtr<T>& AutoPtr<T>::operator=(const AutoPtr<T>& h)
{
    decrUser();
    m_pData = h.m_pData;
    m_nUser = h.m_nUser;
    (*m_nUser)++;
}

? ? ? ? ->运算符重载:

T* operator->()const
    {
        return _ptr;
    }

? ? ? ? *运算符重载:

    //*的重载
    T& operator*()const
    {
        return *_ptr;
    }

5. 小结

??????????在我们使用类模板时,只有当代码中使用了类模板的一个实例的名字,而且上下文环境要求必须存在类的定义时,这个类模板才被实例化。声明一个类模板的指针和引用,不会引起类模板的实例化,因为没有必要知道该类的定义。也了解到了函数模板针对仅参数类型不同的函数;类模板针对仅数据成员和成员函数类型不同的类,可以显著减小源代码的大小并提高代码的灵活性,而不会降低类型安全。由于模板的可重用性和可扩展性,我们可以实现效率很高的代码。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-11-18 11:01:39  更:2021-11-18 11:04:11 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 7:20:50-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码