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.为什么要使用模板?

??C++最重要的特性之一就是代码重用,为了实现代码重用,代码必须具有通用性。通用代码需要不受数据类型的影响,并且可以自动适应数据类型的变化。这种程序设计类型称为参数化程序设计。
??因此C++就有了“模板”这一名词,模板是C++支持参数化程序设计的工具,通过它可以实现参数化多态性。所谓参数化多态性,就是将程序所处理的对象的类型参数化,使得一段程序可以用于处理多种不同类型的对象。

2.模板定义:

??模板是实现代码重用机制的一种工具,它可以实现类型参数化,即把类型定义为参数,从而实现了真正的代码可重用性。

3.模板分类:

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

4.使用模板目的:

??让程序员编写与类型无关的代码。
??注意:模板的声明或定义只能在全局,命名空间或类范围内进行。即不能在局部范围,函数内进行,如不能在main函数中声明或定义一个模板。

二、函数模板

1.什么是函数模板:

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

2.函数模板的定义形式:

template<class 类型参数1, class类型参数2, ...>
返回值类型名 函数名(参数表)
{
	//函数体的定义
}

其中class也能用typename替代

template <typename 类型参数1, typename 类型参数2, ...>

3.一般模板函数用法(compare)

??举一个简单的例子,为了比较两个数a,b的值,并得出最大值需要用到以下的compare函数。
若比较的数值是整形,则:

int compare(int& a, int& b) {
	if (a < b) {
		return b;
	}
	else if (b < a) {
		return a;
	}
	else
		return a;
}

若比较的数值是double浮点型,则:

double compare(double& a, double& b) {
	if (a < b) {
		return b;
	}
	else if (b < a) {
		return a;
	}
	else
		return a;
}

??可以看到针对不同类型的变量我们都得重新再编写一次compare函数,非常的麻烦。
??那么,有什么办法可以让我们只需要编写一次compare函数就能够比较不同变量类型的数值呢?这时候就要用到我们的模板函数了。

根据模板函数的定义形式我们重新对compare函数进行定义:

T compare(T& a, T& b) {
	if (a < b) {
		return b;
	}
	else if (b < a) {
		return a;
	}
	else
		return a;
}

接下来通过main函数运行不同的变量参数:

int main() {
	int a = 1;//int整形
	int b = 2;
	int max_int = compare(a, b);
	cout <<"int整型最大数为:"<<max_int<< endl;
	double c = 1.11;//double浮点型
	double d = 1.12;
	double max_double = compare(c, d);
	cout << "double浮点型最大数为:" << max_double << endl;
}

运行截图:
在这里插入图片描述
可以看到通过模板函数我们只需要在main函数定义好类型便能直接调用compare函数。

4.特化模板函数用法

??使用模板函数时并不是所有的变量类型都适用,有时候会遇到一些特殊的类型需要特殊处理,不能直接使用当前的模板函数,所以此时我们就需要对该类型特化出一个模板函数(就是写出一个模板函数专门给该类型使用)
??模板还分为全特化和偏特化两类,但函数模板只有全特化,因为偏特化的功能可以通过函数的重载完成。
模板函数的全特化:

template<>//全特化,此处为空
char compare<char>(char& a1, char& b1) {
	if (a1 < b1) {
		return b1;
	}
	else if (b1 < a1) {
		return a1;
	}
	else
		return a1;
}

三、类模板(Queue)

1.什么是类模板:

??使用template关键字不但可以定义函数模板,也可以定义类模板,类模板代表一族类,是用来描述通用数据或处理方法的机制,它使类中的一些数据成员和成员函数的参数或返回值可以取任意的数据类型。类模板可以说是用类生成类,减少了类的定义数量。

2.类模板的定义形式:

template <类型形式及参数> 
class 类模板名{
		//类成员声明
}

若要在类模板以外定义其成员函数,则要采用以下的形式:

template <类型形式及参数> 
类型名 类名<模板参数标识符列表>::函数名(参数表)

3.模板类的用法

(一)、Queue,QueueItem类

??如下例子所示,定义了模板类Queue和模板类QueueItem

template<class Type> 
class QueueItem{  //定义偏特化模板类queueitem
     QueueItem(const Type &t):item(t), next(0){}//构造函数,初始化item、next,&t是为了使传入变量完整
     Type item;
     QueueItem * next;
     friend class Queue<Type>;//定义友元类queue,可以访问queueitem的成员
     friend ostream& operator<<(ostream& os, const Queue<Type> &q);
 public:
     QueueItem<Type>* operator++(){
         return next;
     }
     Type & operator*(){
         return item;
     }
 };
 
template<class Type>
class Queue {      //定义偏特化模板类queue
public:
    Queue() :head(0), tail(0) {}
    Queue(const Queue& q) :head(0), tail(0) {
        copy_items(q);
    }
    template<class It>     //定义模板函数,改变变量类型为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);
};

(二)、成员模板函数

??代码如下,创建了三个成员模板函数,destroy(),pop()push(),对应的功能分别是清空所有数据,删除队列最后一个数据以及为队列添加数据

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;//如果对象queueitem为空则为其生成一个队列,头为当前的queue对象
    }
    else { //如果对象不为空,则在其队列的后面加上此queue对象
        tail->next = pt;
        tail = pt;
    }
}

(三)、模板特化

??前面已经讲解了一下什么是模板特化,模板函数的特化又与模板类的特化有所不同。模板函数只能全特化,而模板类既有全特化又有偏特化。
??全特化即将所有模板类型都进行特化,例如将上述程序的Type用int来代替;
??偏特化即对模板类型做一些限制,偏特化分为两种,部分特化和类型范围限制。
模板成员函数全特化:

template<>     //全特化函数,定义当为字符类型时的函数模板,头文件声明
inline void Queue<const char*>::push(const char* const& val);
template<>
inline void Queue<const char*>::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;
}

模板类特化:

//模板类特化
template<> 			//<>里为空
class 类名<特化类型> //<>里指明具体类型
{
	类成员声明
};

4.运行测试Queue类

void TestQueue()
{
    Queue<int> qt;
    double d = 3.3;
    qt.push(1);
    qt.push(d);     //问题:模板函数并没有生成double类型的push
    qt.push(10);
    cout << endl;
    cout << qt;
    short data[5] = { 0,3,6,9 };
    Queue<int> qi(data, data + 5);
    cout << endl;
    cout << qi;

    vector<int> vi(data, data + 5);
    qi.assign(vi.begin(), vi.end());
    cout << endl;
    cout << qi;
    Queue<const char*> q1;
    q1.push("I'm");
    q1.push("come");
    q1.push("from");
    q1.push("JMU");
    cout << endl;
    cout << q1;

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

运行截图:
在这里插入图片描述

四、类模板实现(AutoPtr类)

1.构造函数

template<class T>
AutoPtr<T>::AutoPtr(T* pData)
{
	m_pData = pData;
	m_nUser = new int(1);
}

2.析构函数

~AutoPtr()
	{
		decrUser();
	}
void 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.拷贝构造函数

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

4.等号、->、*等运算符重载

智能指针可以跟普通的指针一样,使用"*“和”->"等操作

AutoPtr<T>& operator=(const AutoPtr<T>& h);
T* operator->()
{
	return m_pData;
}
T& operator*()
{
	return *m_pData;
}
const T& operator *()const
{
	return *m_pData;
}
const T* operator ->()const
{
	return m_pData;
}
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)++;
}

5.主函数调用AutoPtr

#include<iostream>
#include <vector>
#include "autoptr.h"
#include "CMatrix.h"
using namespace std;
int main()
{
    AutoPtr<CMatrix> h1;
    double data[6] = {1,2,3,4,5,6};
    h1->Create(2,3,data);
    cout << *h1 << endl;
    AutoPtr<CMatrix> h2(h1);
    (*h2).Set(0,1,10);
    cout << *h1 << endl << *h2;
}

6.运行实现截图

可以看到h2是通过拷贝构造函数(拷贝h1)创建的,所以h2调用Set方法后改变的是同一个地址的值,因此h1也跟着改变了。进而导致h1和h2输出结果相同。
在这里插入图片描述

  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:03: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/28 1:10:59-

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