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

【C++】模板(简单详细)

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

【C++】模板(简单详细)

C++模板
  • 一、模板
    • 1.为什么要使用模板?
    • 2.模板定义:
    • 3.模板分类:
    • 4.使用模板目的:
  • 二、函数模板
    • 1.什么是函数模板:
    • 2.函数模板的定义形式:
    • 3.一般模板函数用法(compare)
    • 4.特化模板函数用法
  • 三、类模板(Queue)
    • 1.什么是类模板:
    • 2.类模板的定义形式:
    • 3.模板类的用法
      • (一)、Queue,QueueItem类
      • (二)、成员模板函数
      • (三)、模板特化
    • 4.运行测试Queue类
  • 四、类模板实现(AutoPtr类)
    • 1.构造函数
    • 2.析构函数
    • 3.拷贝构造函数
    • 4.等号、->、*等运算符重载
    • 5.主函数调用AutoPtr
    • 6.运行实现截图

一、模板 1.为什么要使用模板?

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

2.模板定义:

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

3.模板分类:

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

4.使用模板目的:

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

二、函数模板 1.什么是函数模板:

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

2.函数模板的定义形式:
template
返回值类型名 函数名(参数表)
{
	//函数体的定义
}

其中class也能用typename替代

template 
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整型最大数为:"< 

运行截图:

可以看到通过模板函数我们只需要在main函数定义好类型便能直接调用compare函数。

4.特化模板函数用法

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

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

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

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

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

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

模板类特化:

//模板类特化
template<> 			//<>里为空
class 类名<特化类型> //<>里指明具体类型
{
	类成员声明
};
4.运行测试Queue类
void TestQueue()
{
    Queue 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 qi(data, data + 5);
    cout << endl;
    cout << qi;

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

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

运行截图:

四、类模板实现(AutoPtr类) 1.构造函数
template
AutoPtr::AutoPtr(T* pData)
{
	m_pData = pData;
	m_nUser = new int(1);
}
2.析构函数
~AutoPtr()
	{
		decrUser();
	}
void decrUser();
template
void AutoPtr::decrUser()
{
	--(*m_nUser);
	if ((*m_nUser) == 0)
	{
		delete m_pData;
		m_pData = 0;
		delete m_nUser;
		m_nUser = 0;
	}
}
3.拷贝构造函数
template
AutoPtr::AutoPtr(const AutoPtr& h)
{
	m_pData = h.m_pData;
	m_nUser = h.m_nUser;
	(*m_nUser)++;
}

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

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

AutoPtr& operator=(const AutoPtr& 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
AutoPtr& AutoPtr::operator=(const AutoPtr& h)
{
	decrUser();
	m_pData = h.m_pData;
	m_nUser = h.m_nUser;
	(*m_nUser)++;
}
5.主函数调用AutoPtr
#include
#include 
#include "autoptr.h"
#include "CMatrix.h"
using namespace std;
int main()
{
    AutoPtr h1;
    double data[6] = {1,2,3,4,5,6};
    h1->Create(2,3,data);
    cout << *h1 << endl;
    AutoPtr h2(h1);
    (*h2).Set(0,1,10);
    cout << *h1 << endl << *h2;
}

6.运行实现截图

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

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

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

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