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

C++实验(三)模板

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

C++实验(三)模板

文章目录
  • 一、模板函数(compare)
    • 1.1 一般模板函数
    • 1.2 特化模板函数
  • 二、模板类Queue或Stack
    • 2.1 模板类(Queue,Stack)
    • 2.2 成员模板函数
    • 2.3 模板特化
      • 2.3.1 模板函数特化
      • 2.3.2 模板成员函数特化
      • 2.3.3 模板类特化
    • 2.4 测试
  • 三、模板类AutoPtr
    • 3.1 构造函数
    • 3.2 析构函数
    • 3.3 拷贝构造函数
    • 3.4 等号、->、*等运算符重载
    • 3.5 主函数调用AutoPtr
  • 四、总结


一、模板函数(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 
using namespace std;

template
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 
using namespace std;

template
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 
#include 
using namespace std;

template
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* 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 QueueItem 
{
    QueueItem(const Type& t) : item(t), next(0) {}
    Type item;
    QueueItem* next;
    friend class Queue;
    //输出运算符的重载
    friend ostream& operator<<(ostream& os, const Queue& q);
public:
    QueueItem* operator++() 
    {
        return next;
    }
    Type& operator*() {
        return item;
    }
};
2.2 成员模板函数
template class Queue 
{
public:
    Queue() : head(0), tail(0) {}
    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(); }
    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& q) {
        os << "< ";
        QueueItem* p;
        for (p = q.head; p; p = p->next) 
        {
            os << p->item << " ";
        }
        os << ">";
        return os;
    }
    const QueueItem* Head() const { return head; }
    const QueueItem* End() const { return (tail == NULL) ? NULL : tail->next; }
private:
    QueueItem* head;
    QueueItem* tail;
    void destroy();
    void copy_items(const Queue&);
    template
    void copy_items(It beg, It end);
};

//销毁队列
template void Queue::destroy()
{
    while (!empty()) 
    {
        pop();
    }
}

template void Queue::pop() 
{
    QueueItem* p = head;
    head = head->next;
    delete p;
}
template void Queue::push(const Type& val) 
{
    QueueItem* pt = new QueueItem(val);
    if (empty()) 
    {
        head = tail = pt;
    }
    else 
    {
        tail->next = pt;
        //元素添加到队列后tail指针指向该元素
        tail = pt;
    }
}
template 
void Queue::copy_items(const Queue& orig) 
{
    for (QueueItem* pt = orig.head; pt; pt = pt->next) 
    {
        push(pt->item);
    }
}
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;
    }
}
2.3 模板特化 2.3.1 模板函数特化

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

2.3.2 模板成员函数特化

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

template < >
void Queue::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::pop()
{
    QueueItem* p = head;
    delete head->item;
    head = head->next;
    delete p;
}
2.3.3 模板类特化

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

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

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

queue.h

#ifndef QUEUE_H
#define QUEUE_H
#include  
using namespace std;

//定义类模板
template class Queue;
template class QueueItem 
{
    QueueItem(const Type& t) : item(t), next(0) {}
    Type item;
    QueueItem* next;
    friend class Queue;
    //输出运算符的重载
    friend ostream& operator<<(ostream& os, const Queue& q);
public:
    QueueItem* operator++() 
    {
        return next;
    }
    Type& operator*() {
        return item;
    }
};

template class Queue 
{
public:
    Queue() : head(0), tail(0) {}
    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(); }
    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& q) {
        os << "< ";
        QueueItem* p;
        for (p = q.head; p; p = p->next) 
        {
            os << p->item << " ";
        }
        os << ">";
        return os;
    }
    const QueueItem* Head() const { return head; }
    const QueueItem* End() const { return (tail == NULL) ? NULL : tail->next; }
private:
    QueueItem* head;
    QueueItem* tail;
    void destroy();
    void copy_items(const Queue&);
    template
    void copy_items(It beg, It end);
};

//销毁队列
template void Queue::destroy()
{
    while (!empty()) 
    {
        pop();
    }
}

template void Queue::pop() 
{
    QueueItem* p = head;
    head = head->next;
    delete p;
}
template void Queue::push(const Type& val) 
{
    QueueItem* pt = new QueueItem(val);
    if (empty()) 
    {
        head = tail = pt;
    }
    else 
    {
        tail->next = pt;
        //元素添加到队列后tail指针指向该元素
        tail = pt;
    }
}

template < >
void Queue::push(const char* const& val);
template < >
void Queue::pop();

template 
void Queue::copy_items(const Queue& orig) 
{
    for (QueueItem* pt = orig.head; pt; pt = pt->next) 
    {
        push(pt->item);
    }
}

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;
    }
}
void TestQueue();

#endif  //QUEUE_H

queue.cpp

#include "queue.h"
#include 
#include

template < >
void Queue::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::pop()
{
    QueueItem* p = head;
    delete head->item;
    head = head->next;
    delete p;
}

void TestQueue()
{
	Queue 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 qi(a, a + 4);
	cout << qi << endl;

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

	Queue q2(q1);
	cout << q2 << endl;
}

main.cpp

#include
#include "queue.h"

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

三、模板类AutoPtr 3.1 构造函数
//构造函数
AutoPtr(T* pData);

template < class T>
AutoPtr::AutoPtr(T* pData)
{
    m_pData = pData;
    //初始化用户数为1
    m_nUser = new int(1);
}
3.2 析构函数
//声明周期结束时调用析构函数
~AutoPtr();

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

template < class T>
void AutoPtr::decrUser()
{
    --(*m_nUser);
    if ((*m_nUser) == 0) 
    {
        //删除数据
        delete m_pData;
        //地址赋为空
        m_pData = 0;
        delete m_nUser;
        m_nUser = 0;
    }
}
3.3 拷贝构造函数
 //拷贝构造函数
AutoPtr(const AutoPtr& h);

template < class T>
AutoPtr::AutoPtr(const AutoPtr& h) 
{
    m_pData = h.m_pData;
    m_nUser = h.m_nUser;
    //用户数加1
    (*m_nUser)++;
}
3.4 等号、->、*等运算符重载
//等号的重载
AutoPtr& operator=(const AutoPtr& h);

template < class T>
AutoPtr& AutoPtr::operator=(const AutoPtr& 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
#include "queue.h"
#include "autoptr.h"
#include "CMatrix.h"
using namespace std;

int main() {
    //创建一个CMatrix类的指针并交给智能指针类进行管理
    AutoPtr 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 h2(h1);
    (*h2).Set(0, 1, 10);
    cout << *h1 << *h2 << endl;

    return 0;
}


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

四、总结

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

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

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

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