C++实验三之模板

一、模板函数

1.1 一般模板函数

在此之前,先说明一下模板的概念:模板就是建立通用的模具,大大提高复用性。需要注意的是:模板不可以直接使用,它只是一个框架。
而对函数模板来说,它的作用是建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型来代表。具体语法如下:

template<typename T>
函数声明或定义

template — 声明创建模板

typename — 表面其后面的符号是一种数据类型,可以用class代替

T — 通用的数据类型,名称可以替换,通常为大写字母
现在用一个案例来进行演示。

#include<iostream>
#include<string>

using namespace std;

template<typename T>
void mySwap(T& a, T& b) {
	T temp = a;
	a = b;
	b = temp;
}

void test01() {
	int a = 10;
	int b = 20;
	mySwap<int>(a, b);
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
}
int main() {
	test01();

	return 0;
}

可以看到,交换成功
在这里插入图片描述
对于不同的数据类型,也可以进行交换:
在这里插入图片描述
另外,使用函数模板时,如果用自动类型推导,不会发生自动类型转换,即隐式类型转换。
如创建加法函数

template<typename T>
T myAdd(T a, T b) {
	return a + b;
}

在这里插入图片描述

报错的原因是使用自动类型推导时,不会发生隐式类型转换
在这里插入图片描述

而加上int,用显示指定类型,可以发生隐式类型转换

1.2 特化模板函数

先创建一个比较函数

template<typename T>
bool myCompare(T a, T b) {
	if (a != b)
		return false;
	else return true;
}

在这里插入图片描述
运行不报错。
但是,能比较类中的成员函数吗?

class Person {
public:
	string m_Name;
	Person(string name) {
	this -> m_Name = name;
	}
};

在这里插入图片描述
生成失败,显然是不能这样使用的。
这时候,我们就引入特化模板函数概念。
模板特化:就是在实例化模板时,对特定类型的实参进行特殊处理,即实例化一个特殊的实例版本,当以特化定义时的形参使用模板时,将调用特化版本。简而言之,就是指定了模板的使用数据类型。
另外,它的优先级高于函数模板。

template<> bool myCompare(Person a,Person b) {
	if (a.m_Name == b.m_Name) {
		return true;
	}
	else return false;
}

在这里插入图片描述
在这里插入图片描述
注意:特化模板类型必须与原模板函数一样。如果原来的一般模板函数类型,如:

template<typename T>
void mySwap(T& a, T& b)

那么在特化时,原来里面带&,特化函数也必须带,否则报错。
在这里插入图片描述

二、模板类Queue或Stack

下面以实现Queue为案例

2.1 成员模板函数

成员模板函数,就是类模板实例化出的对象,向函数传参。
一共有三种传入方式:

  1. 指定传入的类型 — 直接显示对象的数据类型
  2. 参数模板化 — 将对象中的参数变为模板进行传递
  3. 整个类模板化 — 将这个对象类型 模板化进行传递
    在类QueueItem中,首先声明一个模板template class Type ,在这里是将整个类都模板化,其他函数都是为了这个类型的参数做工作。这很好理解:在我们创建队列Queue的时候,就是对一个数据类型进行入队出队,显而易见,里面的操作函数都是在为这一个数据类型做服务的。
    实际上,模板的本质区别就是改变替换参数类型,实现过程也很简单。我的理解是:将固定的变量类型,如int, double…用模板里的全部代替。在调用的时候再把要用的参数名传进。
    以其中一个QueueItem例为例
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;
    }
};

2.2 模板特化

以Queue类为例。
首先介绍模板特化的几种类型:模板函数特化、模板成员函数特化、模板类特化。
在做特化时,同创建一般函数模板一样,都需要在类前做一个模板声明并指定名称,如:template<class Type>,不过括号的里面为空,即:templete<>
,随后在使用过程中,与模板函数一样,将之前写过的Type 参数名改成你想要的,如int,double…就好。

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

三、模板类AutoPtr

在学会了模板的几种使用方法后,下面将进行实战。
首先说一下什么是AutoPtr。智能指针是一个类,这个类的构造函数中传入一个普通指针,析构函数中释放传入的指针。智能指针的类都是栈上的对象,所以当函数(或程序)结束时会自动被释放,于是我们用个简单的案例来实现一下。

 #include <stdio.h>
#include <iostream>

using namespace std;

template <typename T>
class Shared_ptr
{
private:
    size_t* m_count;
    T* m_ptr;

public:
    //构造函数
    Shared_ptr() : m_ptr(nullptr), m_count(new size_t)
    {}

    Shared_ptr(T* ptr) : m_ptr(ptr), m_count(new size_t)
    {
        cout << "空间申请:" << ptr << endl;
        *m_count = 1;
    }

    //析构函数
    ~Shared_ptr()
    {
        --(*m_count);
        if (*m_count == 0)
        {
            cout << "空间释放:" << m_ptr << endl;
            delete m_ptr;
            delete m_count;
            m_ptr = nullptr;
            m_count = nullptr;
        }
    }

    //拷贝构造函数
    Shared_ptr(const Shared_ptr& ptr)
    {
        m_count = ptr.m_count;
        m_ptr = ptr.m_ptr;
        ++(*m_count);
    }

    //拷贝赋值运算符
    void operator=(const Shared_ptr& ptr)
    {
        Shared_ptr(std::move(ptr));
    }

    //移动构造函数
    Shared_ptr(Shared_ptr&& ptr) : m_ptr(ptr.m_ptr), m_count(ptr.m_count)
    {
        ++(*m_count);
    }

    //移动赋值运算符
    void operator=(Shared_ptr&& ptr)
    {
        Shared_ptr(std::move(ptr));
    }

    //解引用运算符
    T& operator*()
    {
        return *m_ptr;
    }

    //箭头运算符
    T* operator->()
    {
        return m_ptr;
    }

    //重载布尔值操作
    operator bool()
    {
        return m_ptr == nullptr;
    }

    T* get()
    {
        return m_ptr;
    }

    size_t use_count()
    {
        return *m_count;
    }

    bool unique()
    {
        return *m_count == 1;
    }

    void swap(Shared_ptr& ptr)
    {
        std::swap(*this, ptr);
    }

};




int main()
{
    Shared_ptr<int> p1(new int);
    *p1 = 222;
    cout << "值:" << *p1 << " 引用计数:" << p1.use_count() << endl;
    {
        Shared_ptr<int> p2(p1);
        *p2 = 333;
        cout << "值:" << *p2 << " 引用计数:" << p1.use_count() << endl;

        Shared_ptr<int> p3(p2);
        *p3 = 444;
        cout << "值:" << *p3 << " 引用计数:" << p1.use_count() << endl;
    }

    cout << "引用计数:" << p1.use_count() << endl;

    Shared_ptr<string> q1(new string("我是string1"));
    cout << (*(q1)).c_str() << endl;

    Shared_ptr<string> q2(new string("我是string2"));
    q2.swap(q1);
    cout << (*(q1)).c_str() << endl;

    return 0;
}

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值