浅析C++中模板的那点事_C 语言

1.什么是模板

假设现在我们完成这样的函数,给定两个数x和y求式子x^2 + y^2 + x * y的值 .考虑到x和y可能是 int , float 或者double类型,那么我们就要完成三个函数:

int fun(int x,int y);
float fun(float x,float y);
double fun(double x,double y);

并且每个fun函数内部所要完成的操作也是极其的相似。如下:

复制代码 代码如下:

int fun(int x,int y)
{
    int tmp = x *x + y * y + x * y;
    return tmp;
}
float fun(float x,float y)
{
    float tmp = x *x + y * y + x * y;
    return tmp;
}
double fun(double x,double y)
{
    double tmp = x *x + y * y + x * y;
    return tmp;
}

可以看出,上面的三个函数体除了类型不一样之外,其他的完全一样,那么如果能够只写一个函数就能完成上面的三个函数的功能该多好呢?如果从这三个函数提炼出一个通用函数,而它又适用于这三种不同类型的数据,这样会使代码的重用率大大提高。实际上C++中的模板正好就是来解决这个问题的。模板可以实现类型的参数化(把类型定义为参数),从而实现了真正的代码可重用性。C++中的模板可分为函数模板和类模板,而把函数模板的具体化称为模板函数,把类模板的具体化成为模板类。下面让我们分别看看什么是函数模板和类模板吧~~~

2.模板函数

实际上我们利用函数模板,只需要一个函数就可能完成上面的三个函数了,千言万语不如看代码:

复制代码 代码如下:

#include <iostream>

using namespace std;

template <typename T>
T fun(T x,T y)
{
    T tmp = x *x + y * y + x * y;
    return tmp;
}
int main()
{
    int x1 = 1,y1 = 4;
    float x2 = 1.1 , y2 = 2.2;
    double x3 = 2.0 , y3 = 3.1;
    cout<<fun(x1,y1)<<endl;
    cout<<fun(x2,y2)<<endl;
    cout<<fun(x3,y3)<<endl;
    return 0;
}

运行结果:

如此利用模板,我们很轻而易举的达到了我们的目的,而这也大大的提高了代码的可重用性,这也让我们想起了STL中的那些算法了吧,这些算法使用多种的数据类型。实际上STL即使模板的重要应用了。

现在我们想,如果上面的代码这样调用fun(x1,y2)会怎么样呢?点击编译会出现这样的错误:

可以看到编译编译出现问题的是fun(x1,y2),说的意思就是没有对应的函数,要么x1和y2都是int型,要么x1和y2都是float型。那么我为什么要说一下这样一种情况呢?主要是为了引出模板也可以同时使用两个:

复制代码 代码如下:

#include <iostream>

using namespace std;

template <typename T1 , typename T2>
T2 fun(T1 x,T2 y)
{
    T2 tmp = x *x + y * y + x * y;
    return tmp;
}
int main()
{
    int x1 = 1,y1 = 4;
    float x2 = 1.1 , y2 = 2.2;
    double x3 = 2.0 , y3 = 3.1;
    cout<<fun(x1,y1)<<endl;
    cout<<fun(x2,y2)<<endl;
    cout<<fun(x3,y3)<<endl;
    cout<<fun(x1,y2)<<endl;
    return 0;
}

运行结果:

当使用两个模板时,为什么fun(x1,y1)也能正确运行呢?因为当进行这个调用时,T1 = int ,T2 = int。所以这种调用也是没有问题的。

提到函数想到重载是很自然的吧,那么模板函数能不能重载呢?显然是能的了,还是看代码:

复制代码 代码如下:

#include <iostream>

using namespace std;

template <typename T1 , typename T2>
T2 fun(T1 x,T2 y)
{
    cout<<"调用了两个个参数的 fun 函数 ^^ "<<endl;
    T2 tmp = x *x + y * y + x * y;
    return tmp;
}
template <typename T>
T fun(T x , T y , T z)
{
    cout<<"调用了三个参数的 fun 函数 ^^ "<<endl;
    T tmp = x * x + y * y + z * z + x * y * z;
    return tmp;
}
int main()
{
    int x1 = 1 , y1 = 4 , z1 = 5;
    float x2 = 1.1 , y2 = 2.2;
    double x3 = 2.0 , y3 = 3.1;
    cout<<fun(x1,y1)<<endl;
    cout<<fun(x2,y2)<<endl;
    cout<<fun(x3,y3)<<endl;
    cout<<fun(x1,y2)<<endl;
    cout<<fun(x1,y1,z1)<<endl;
    return 0;
}

运行结果:

从结果已经能看出来模版函数的重载是没有任何问题的了。那么模板函数和非模板函数之间是否能够重载呢??

复制代码 代码如下:

#include <iostream>

using namespace std;

template <typename T>
T fun(T x,T y)
{
    cout<<"调用了模板函数 ^^ "<<endl;
    T tmp = x * x + y * y + x * y;
    return tmp;
}
int fun(int x,int y)
{
    cout<<"调用了非模板函数 ^^ "<<endl;
    int tmp = x * x + y * y + x * y;
    return tmp;
}

int main()
{
    int x1 = 1 , y1 = 4;
    float x2 = 1.1 , y2 = 2.2;
    cout<<fun(x1,y1)<<endl;
    cout<<fun(x2,y2)<<endl;
    return 0;
}

运行结果:

看以看出模版函数和非模板函数也是可能重载的,那么重载函数的调用顺序是怎么样的呢?实际上是先查找非模板函数,要有严格匹配的非模板函数,就调用非模板函数,找不到适合的非模板函数在和模板函数进行匹配。

到这里,关于模板就说这些吧~~~~

3.模板类

要是理解了模版函数,模板类就相当的简单了,只不过模版函数是对函数中的类型使用模板,而模板类是对类中的类型使用模板,这我就不多说了,下面的代码是我以前利用模板写的单链表,这个是模板的典型应用:(测试过)

复制代码 代码如下:

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

template <class T>
struct SLNode
{
    T data;
    SLNode<T> *next;
    SLNode(SLNode<T> *nextNode=NULL)
    {
        next = nextNode;
    }
    SLNode(const T &item,SLNode<T> *nextNode=NULL)
    {
        data = item;
        next = nextNode;
    }
};

template <class T>
class SLList
{
private:
    SLNode<T> *head;
    SLNode<T> *tail;
    SLNode<T> *currptr;
    int size;
public:
    SLList();
    SLList(const T &item);
    ~SLList();
    bool IsEmpty()const;
    int Length()const;
    bool Find(int k,T &item)const;
    int Search(const T &item)const;
    void InsertFromHead(const T &item);
    void InsertFromTail(const T &item);
    bool DeleteFromHead(T &item);
    bool DeleteFromTail(T &item);
    void Insert(int k,const T &item);
    void Delete(int k,T &item);
    void ShowListMember();
};
//构造函数
template <class T>
SLList<T>::SLList()
{
    head = tail = currptr = new SLNode<T>();
    size = 0;
}
//构造函数
template <class T>
SLList<T>::SLList(const T &item)
{
    tail = currptr = new SLNode<T>(item);
    head = new SLNode<T>(currptr);
    size = 1;
}
//析构函数
template <class T>
SLList<T>::~SLList()
{
     SLNode<T> *temp;
    while(!IsEmpty())
    {
        temp = head->next;
        head->next = temp->next;
        delete temp;

    }
}
//判断链表是否为空
template <class T>
bool SLList<T>::IsEmpty()const
{
    return head->next == NULL;
}
//返回链表的长度
template <class T>
int SLList<T>::Length()const
{
     return size;
}
//查找第k个节点的阈值
template <class T>
bool SLList<T>::Find(int k,T &item)const
{
    if(k < 1)
    {
        cout<<"illegal position !"<<endl;
    }
    SLNode<T> *temp = head;
    int count = 0;
    while(temp != NULL && count < k)
    {
        temp = temp->next;
        count++;
    }
    if(temp == NULL)
    {
        cout<<"The list does not contain the K node !"<<endl;
        return false;
    }
    item = temp->data;
    return true;
}
//查找data阈值为item是表的第几个元素
template <class T>
int SLList<T>::Search(const T &item)const
{
    SLNode<T> *temp = head->next;
    int count = 1;
    while(temp != NULL && temp->data != item)
    {
        temp = temp->next;
        count++;
    }
    if(temp == NULL)
    {
        cout<<"The node does not exist !"<<endl;
        return -1;
    }
    else
    {
        return count;
    }
}
//从表头插入
template <class T>
void SLList<T>::InsertFromHead(const T &item)
{   
    if(IsEmpty())
    {
        head->next = new SLNode<T>(item,head->next);
        tail = head->next;
    }
    else
    {
        head->next = new SLNode<T>(item,head->next);
    }
    size++;
}
//从表尾插入
template <class T>
void SLList<T>::InsertFromTail(const T &item)
{
    tail->next = new SLNode<T>(item,NULL);
    tail = tail->next;
    size++;
}
//从表头删除
template <class T>
bool SLList<T>::DeleteFromHead(T &item)
{
    if(IsEmpty())
    {
        cout<<"This is a empty list !"<<endl;
        return false;
    }
    SLNode<T> *temp = head->next;
    head->next = temp->next;
    size--;
    item = temp->data;
    if(temp == tail)
    {
        tail = head;
    }
    delete temp;
    return true;
}
//从表尾删除
template <class T>
bool SLList<T>::DeleteFromTail(T &item)
{
    if(IsEmpty())
    {
        cout<<"This is a empty list !"<<endl;
        return false;
    }
    SLNode<T> *temp = head;
    while(temp->next != tail)
    {
        temp = temp->next;
    }
    item = tail->data;
    tail = temp;
    tail->next=NULL;
    temp = temp->next;
    delete temp;
    size--;
    return true;
}
//在第k个节点后插入item值
template <class T>
void SLList<T>::Insert(int k,const T &item)
{
    if(k < 0 || k > size)
    {
        cout<<"Insert position Illegal !"<<endl;
        return;
    }
    if(k == 0)
    {
        InsertFromHead(item);
        return;
    }
    if(k == size)
    {
        InsertFromTail(item);
        return;
    }
    SLNode<T> *temp = head->next;
    int count = 1;
    while(count < k)
    {
        count++;
        temp = temp->next;
    }
    SLNode<T> *p = temp->next;
    temp->next = new SLNode<T>(item,p);
    size++;
}
//删除第k个节点的值,保存在item中
template <class T>
void SLList<T>::Delete(int k,T &item)
{
    if(k <= 0 || k > size)
    {
        cout<<"Ileegal delete position !"<<endl;
        return;
    }
    if(k == 1)
    {
        DeleteFromHead(item);
        return;
    }
    if(k == size)
    {
        DeleteFromTail(item);
        return;
    }
    SLNode<T> *temp = head->next;
    int count = 1;
    while(count < k-1)
    {
        count++;
        temp = temp->next;
    }
    SLNode<T> *p = temp->next;
    temp->next = p->next;
    p->next = NULL;
    item = p->data;
    delete p;
    size--;
}
template <class T>
void SLList<T>::ShowListMember()
{
    cout<<"List Member : ";
    SLNode<T> *temp = head->next;
    while(temp != NULL)
    {
        cout<<temp->data<<" ";
        temp = temp->next;
    }
    cout<<endl;
}

/*
1.引入了InsertFronHead,InsertFromTail,DeleteFromHead和DeleteFromTail用来实现Insert和Delete函数,是一个比较好的方法。

2.SLNode(T &item,SLNode<T> *nextNode)这个构造函数设计的非常巧妙,便于其他成员函数的实现。

3.插入,删除分为:表头,表尾,中间插入(删除)三种情况
*/

int main()
{
    int item;
    SLList<int> list(12);

    list.Insert(0,11);
    cout<<"list number:"<<list.Length()<<endl;
    list.ShowListMember();

    list.Insert(2,14);
    cout<<"list number:"<<list.Length()<<endl;
    list.ShowListMember();

    list.Insert(2,13);
    cout<<"list number:"<<list.Length()<<endl;
    list.ShowListMember();

    list.Delete(2,item);
    cout<<"item = "<<item<<endl;
    cout<<"list number:"<<list.Length()<<endl;
    list.ShowListMember();

    list.Delete(1,item);
    cout<<"item = "<<item<<endl;
    cout<<"list number:"<<list.Length()<<endl;
    list.ShowListMember();

    list.Delete(2,item);
    cout<<"item = "<<item<<endl;
    cout<<"list number:"<<list.Length()<<endl;
    list.ShowListMember();
    return 0;
}

利用模板的好处是,SLList中的数据可以是任意的数据类型,这也就是泛型编程的概念了吧~~~~

时间: 2024-09-10 10:54:59

浅析C++中模板的那点事_C 语言的相关文章

浅析C++中memset,memcpy,strcpy的区别_C 语言

复制代码 代码如下: #include <stdio.h>#include <stdlib.h>#include <string.h>#include <assert.h> //memcpy:按字节复制原型:extern void* memcpy(void *dest,void *src,unsigned int count)//功能:由src所指内存区域复制count个字节到dest所指的内存区域://同strcpyvoid *memcpy_su(void

浅析VC++中的头文件包含问题_C 语言

在一些大的工程中,可能会包含几十个基础类,免不了之间会互相引用(不满足继承关系,而是组合关系).也就是需要互相声明.好了,这时候会带来一些混乱.如果处理得不好,会搞得一团糟,根据我的经验,简单谈谈自已的处理办法: 编码时,我们一般会尽量避免include头文件,而是采用声明 class XXX.但有时候还是必须用Include头文件,那么,两者的划分在于什么呢? 应该是很明确的,但书上好像都少有提及. 首先:我们要明白为什么要用声明取代头文件包含:对了,是为了避免无必要的重编译(在头文件发生变更

浅析c++中new和delete的用法_C 语言

new和delete运算符用于动态分配和撤销内存的运算符 new用法: 1.开辟单变量地址空间1)new int;  //开辟一个存放数组的存储空间,返回一个指向该存储空间的地址.int *a = new int 即为将一个int类型的地址赋值给整型指针a.  2)int *a = new int(5) 作用同上,但是同时将整数赋值为5 2. 开辟数组空间一维: int *a = new int[100];开辟一个大小为100的整型数组空间二维: int **a = new int[5][6]三

c++中for双循环的那些事_C 语言

情况1:如下,这样我们会发现,n输出为100,虽然两层循环的标识符都是i,然是两个做管辖的范围不同,具体情况不明~~~求大神解释 复制代码 代码如下: int main(int argc,char* argv[]){    int n=0;    int mx;    for (int i=0;i<10;i++)    {        for (int i=0;i<10;i++)        {            n++;        }    }    cout<<n&

解读C++编程中类模板的三种特化_C 语言

1.类模板显式特化为了进行特化,首先需要一个通用的版本,称主模板.主模板使用了标准库堆算法.  堆 是一种线性化的树形结构,将一个值压入一个堆中, 实际上等于将该值插入到一个树形结构中;将一个值从堆中取出就等于移除并返回堆中最大值.但在处理字符的指针时会碰钉子.堆将按照指针的值进行组织. 我们可以提供一个显式特化版本解决此问题(例1)如果希望除了一个针对const char*的Heap外,还希望提供一个针对char *的Heap;(例2) //主模板 template <typename T>

C++模板类的用法实例_C 语言

本文实例讲述了C++中模板类的用法,分享给大家供大家参考.具体方法如下: //#include "StdAfx.h #ifndef __AFXTLS_H__ #define __AFXTLS_H__ #include <Windows.h> class CSimpleList { public: CSimpleList(int nNextOffset=0); void Construct(int nNextOffset); //接口 BOOL IsEmpty() const; voi

c++ 中__declspec 的用法详解_C 语言

c++ 中__declspec 的用法如下,想要了解的继续往下看吧. 语法说明: __declspec ( extended-decl-modifier-seq ) 扩展修饰符: 1:align(#) 用__declspec(align(#))精确控制用户自定数据的对齐方式 ,#是对齐值. e.g __declspec(align(32)) struct Str1{ int a, b, c, d, e; }; 它与#pragma pack()是一对兄弟,前者规定了对齐的最小值,后者规定了对齐的最

简单讲解C语言中宏的定义与使用_C 语言

宏定义是预编译功能的一种, 预编译又称为预处理, 是为编译做的预备工作的阶段.处理#开头的指令, 比如拷贝 #include 包含的文件代码,#define宏定义的替换,条件编译等. 使用宏定义的好处:使用宏定义的好处:可提高程序的通用性和易读性,减少不一致性,减少输入错误和便于修改.例如 π 这个常量,我们有时候会在程序的多个地方使用,如果每次使用都重新定义,一来比较麻烦,二来容易出错,所以我们可以把 π 做成宏定义来使用.   语法说明: (1)宏名一般用大写 (2)使用宏可提高程序的通用性

C语言中static的作用及C语言中使用静态函数有何好处_C 语言

想了解Java中static关键字的作用和用法详细介绍,请点击此处了解详情. 在C语言中,static的字面意思很容易把我们导入歧途,其实它的作用有三条,分别是: 一是隐藏功能,对于static修饰的函数和全局变量而言 二是保持持久性功能,对于static修饰的局部变量而言. 三是因为存放在静态区,全局和局部的static修饰的变量,都默认初始化为0 下面我逐一给大家介绍: (1)先来介绍它的第一条也是最重要的一条:隐藏. 当我们同时编译多个文件时,所有未加static前缀的全局变量和函数都具有