C++实验(三)模板


一、模板函数(compare)

定义:函数模板不是一个实在的函数,编译器不能为其生成可执行代码。定义函数模板后只是一个对函数功能框架的描述,当它具体执行时,将根据传递的实际参数决定其功能。

1.1 一般模板函数

面向对象的继承和多态机制有效提高了程序的可重用性和可扩充性。在程序的可重用性方面,程序员还希望得到更多支持。举一个最简单的例子,为了比较两个整型变量的值,需要写下面的 compare函数:

int compare(int v1, int v2) 
{
    if (v1 < v2)    return -1;
    if (v1 > v2)    return 1;
    return 0;
}

为了比较两个 double 型变量的值,还需要编写下面的 compare函数:

int compare(double v1, double v2) 
{
    if (v1 < v2)    return -1;
    if (v1 > v2)    return 1;
    return 0;
}

如果还要比较两个 char 型变量的值……都需要再编写 compare函数。而这些 compare函数除了处理的数据类型不同外,形式上都是一样的。能否只写一遍 compare函数,就能用来比较各种类型的变量的值呢?继承和多态显然无法解决这个问题。因此,“模板”的概念就应运而生了。

即只需编写一次函数模板,然后基于调用函数时提供的参数类型,C++编译器将自动产生相应的函数来处理该类型的数据。如以下代码实现比较 int、double类型的变量的值。

#include <iostream>
using namespace std;

template<class Type>
int compare(const Type& v1, const Type& v2) 
{
    if (v1 < v2)    return -1;
    if (v1 > v2)    return 1;
    return 0;
}
int main() {
    
    int i1 = 1, i2 = 2;
    double d1 = 2.2, d2 = 1.1;
    cout << compare(i1, i2) << endl;
    cout << compare(d1, d2) << endl;
    return 0;
}

在这里插入图片描述

1.2 特化模板函数

定义:函数模板特化是在一个统一的函数模板不能在所有类型实例下正常工作时,需要定义类型参数在实例化为特定类型时函数模板的特定实现版本。目前的标准中,模板函数只能全特化,没有偏特化
比如上面的比较函数虽然能实现int、double类型变量的值的比较,但却不支持char*(string)类型。

#include <iostream>
using namespace std;

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

    const char* a = "hi", * b = "world";
    cout << compare(a, b) << endl;
    return 0;
}

在这里插入图片描述

很明显,与我们预期的结果不一致。所以我们必须对其进行特化,以让它支持两个字符串的比较,因此我们实现了如下的特化函数。

#include <iostream>
#include <cstring>
using namespace std;

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* v1, const char* v2)
{
    return strcmp(v1, v2);
}

int main() { 
    const char* a = "hi", * b = "world";
    cout << compare(a, b) << endl;
    return 0;
}

在这里插入图片描述
函数模版的特化,当函数调用发现有特化后的匹配函数时,会优先调用特化的函数,而不再通过函数模版来进行实例化。

二、模板类Queue或Stack

定义:类模板的使用能使用户为类定义一种模式,使得类中的某些数据出成员、某些成员函数的参数、返回值或局部变量能取不同类型(包括系统预定义的和用户自定义的)

现要实现一个队列的出队、入队、销毁队的模板类Queue,然后通过具体参数类型对类模板进行实例化并实现相关数据类型的数据处理。

2.1 模板类(Queue,Stack)

QueueItem

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

2.2 成员模板函数

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指针指向该元素
        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> 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 模板特化

2.3.1 模板函数特化

定义和用法在上面1.2 特化模板函数 已经描述过了

2.3.2 模板成员函数特化

现为模板类Queue的成员函数push()和pop()方法实现特化,使其能够实现字符串的入队和出队操作,

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

2.3.3 模板类特化

类模板的特化:与函数模板类似,当类模板内需要对某些类型进行特别处理时,使用类模板的特化。例如:

这里归纳了针对一个模板参数的类模板特化的几种类型

  • 一是特化为绝对类型
  • 二是特化为引用,指针类型
  • 三是特化为另外一个类模板

2.4 测试

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指针指向该元素
        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;
    }
}
void TestQueue();

#endif  //QUEUE_H

queue.cpp

#include "queue.h"
#include <vector>
#include<cstring>

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

void TestQueue()
{
	Queue<int> qt;
	double d = 3.3;
	qt.push(1);
	qt.push(d);
	qt.push(10);
	cout << qt << endl;

	short a[5] = { 0, 3, 6, 9 };
	Queue<int> qi(a, a + 4);
	cout << qi << endl;

	vector<int> vi(a, a + 4);
	qi.assign(vi.begin(), vi.end());
	cout << qi << endl;
	Queue<const char*> q1;
	q1.push("hi");
	q1.push("I'm");
	q1.push("LiaoNanan");
	cout << q1 << endl;

	Queue<const char*> q2(q1);
	cout << q2 << endl;
}

main.cpp

#include<iostream>
#include "queue.h"

using namespace std;
int main() 
{
    TestQueue();
    return 0;
}

在这里插入图片描述

三、模板类AutoPtr

3.1 构造函数

//构造函数
AutoPtr(T* pData);

template < class T>
AutoPtr<T>::AutoPtr(T* pData)
{
    m_pData = pData;
    //初始化用户数为1
    m_nUser = new int(1);
}

3.2 析构函数

//声明周期结束时调用析构函数
~AutoPtr();

template < class T>
AutoPtr<T>::~AutoPtr()
{
    decrUser();
}

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 拷贝构造函数

 //拷贝构造函数
AutoPtr(const AutoPtr<T>& h);

template < class T>
AutoPtr<T>::AutoPtr(const AutoPtr<T>& h) 
{
    m_pData = h.m_pData;
    m_nUser = h.m_nUser;
    //用户数加1
    (*m_nUser)++;
}

3.4 等号、->、*等运算符重载

//等号的重载
AutoPtr<T>& operator=(const AutoPtr<T>& h);

template < class T>
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

main.cpp

#include<iostream>
#include "queue.h"
#include "autoptr.h"
#include "CMatrix.h"
using namespace std;

int main() {
    //创建一个CMatrix类的指针并交给智能指针类进行管理
    AutoPtr<CMatrix> h1(new CMatrix);
    double data[6] = { 1,2,3,4,5,6 };
    //生成一个2行3列的数组
    h1->Create(2, 3, data);
    cout << *h1 << endl;
    //h2(拷贝构造函数的使用)和h1指向的是同一个地方
    AutoPtr<CMatrix> h2(h1);
    (*h2).Set(0, 1, 10);
    cout << *h1 << *h2 << endl;

    return 0;
}

在这里插入图片描述
h1通过数组创建后,h2通过拷贝构建函数方式拷贝了h1,此时h2和h1两个指针指向的是同一个地址,之后调用h2的Set方法修改了部分值,导致h1和h2同时发生了改变

四、总结

模板的使用增强了代码编写的灵活性、可重用性和可扩展性;可以把用同一个算法去适用于不同类型数据,在编译时确定具体的数据类型,大大减少开发时间,节约了时间和精力。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值