C++模版template

模版

模版相关知识对下学期的数据结构学习有重要作用,比如在链表、栈、队列的学习中频繁应用。下面是一段较为完整的链栈结构:

ps. 不需要看懂这段代码,它只是为了说明模版在后续学习中的作用

#include <iostream>
using namespace std;

template<class T> //模版
class linkStack{
private:
    struct Node{
        T data;  //模版
        Node* next; 
        Node(){next=NULL;} 
        Node(const T &value, Node *p=NULL){ //模版
            data=value, next=p;
        }
    };
    Node* top;
public:
    linkStack(){top=NULL;}
    ~linkStack(){clear();}
    void clear();
    bool empty(){return top==NULL;}
    int size();
    void push(const T &value); //模版
    void pop();
    T getTop(); //模版
};

template<class T> //模版
void linkStack<T>::push(const T &value) { //模版
    Node *p=new Node();
    p->data=value;
    p->next=top;
    top=p;
}

template<class T> //模版
void linkStack<T>::pop() { //模版
    if(empty())return;
    top=top->next;
}

template<class T> //模版
T linkStack<T>::getTop() { //模版
    return top->data;
}

template<class T> //模版
void linkStack<T>::clear() { //模版
    Node *p=NULL;
    while (!empty()){
        top=top->next;
    }
}

template<class T> //模版
int linkStack<T>::size() { //模版
    int count=0;
    Node *p=top;
    while(p){
        count++;
        p=p->next;
    }
    return count;
}

由于模版并不是课本上的要求课程(貌似),而更像是一个辅助理解的工具,因此在下文中,我将介绍模版的含义,函数模版、类模版的基本使用等知识,不会包含非类型形参等更深的知识,且由于是自己瞎总结的,所以逻辑上可能不是很严谨,知识也不是很成体系。如果有任何问题,或发现任何错误,可以评论或直接找我。

引例

直接讲或许会有点难懂,我们先看一道例题:

编写一个函数findMax,使它可以返回两个同类型数/字符的较大值。

如果只考虑三个类型intchardouble,我们需要用到函数重载,写三个同名函数:

int findMax(int a, int b){
    if(a>b)return a;
    else return b;
}

char findMax(char a, char b){
    if(a>b)return a;
    else return b;
}

double findMax(double a, double b){
    if(a>b)return a;
    else return b;
}

这样才能保证在主函数中,填入任何一个类型的数,函数都能正常运行:

cout<<findMax(1,2)<<endl; //整型
cout<<findMax(1.1,2.2)<<endl; //双浮点数
cout<<findMax('a','c'); //字符

输出:

2
2.2
c

但事实上,需要考虑的类型远不止这三种,除了系统自带的float long long unsigned等,还需要有我们自己定义的类/结构体

难道我们必须把每种类型都列上,才能实现目的吗?

不难发现,上面的三个函数除了函数类型、参数类型不一样之外,其他的操作都是一样的没错我就是复制粘贴的。

那我们自然可以提出一个问题:可不可以先用一个抽象的类型T写函数,然后调用的时候再把它具像化,就像下面这样:

T findMax(T t1, T t2){
    if(t1>t2)return t1;
    else return t2;
}

答案当然是可以的,这就要用到这篇文章想讲的核心知识——模版template

模版的正式介绍

模版是一种对类型进行参数化的工具,使用模版可以实现为函数或类声明一种一般模式,使得函数的参数、返回值或类的数据成员、成员函数取得任意类型,即可以编写与类型无关的代码

从上面的介绍中,可以看出,当我们用模版时:

  1. 使用之前,声明“一种模式”
  2. 具体调用时,代入“任意类型”

声明模版

使用关键字template <class T1, class T2, ...>来声明模版,在这个关键字出现之后的一定区域内,可以使用抽象类型T1T2、…

我们来尝试用模版解决引例中findMax函数的问题:

template<class T> //声明抽象类型T
T findMax(T t1, T t2){ //使用抽象类型T
    if(t1>t2)return t1;
    else return t2;
}
int main()
{
    cout<<findMax(1,2)<<endl; 
    //填入整数,相当于告诉程序,T具像化为整型,下同

    cout<<findMax(1.1,2.2)<<endl;
    cout<<findMax('a','c');
    return 0;
}

输出:

2
2.2
c

ps. 这里的“抽象类型”只是我为了更形象地描述它的作用,因而自己起了个名字,与面向对象中的“抽象类”不是一个概念。

模版的分类

模版一般分为两种,函数模版类模版

  • 函数模版用于仅参数类型/返回值类型不同的函数
  • 类模版用于仅数据成员和成员函数不同的类

上面的引例属于函数模版

函数模版

template <class T1, class T2, ...>
函数类型 函数名(参数1, 参数2, ...){
    函数体
}

调用时,不需额外操作,只需直接填入相应参数即可。除上述引例外,再给出一个例子:

template<class T> //声明抽象类型T
T Swap(T &t1, T &t2){ //交换两个数
    T tmp;
    tmp=t1;
    t1=t2;
    t2=tmp;
}
int main()
{
    int a=1,b=2;
    Swap(a,b); //直接填入——整型的a和b
    cout<<"a="<<a;
    return 0;
}

输出

a=2

类模版

类模版与函数模版相似,也是在类的定义中引入抽象类型T,并在后续的使用中具像化不同的类型。

类模版的基本格式

template <class T> //声明抽象类T
class point{ 
private:
    T a,b; //T类型的成员变量
public:
    point(T A, T B){ //参数类型为T的有参构造函数
        a=A, b=B;
    }
    point(){} //无参构造函数
    T findMax(){ //返回值类型为T的内联成员函数
        if(a>b)return a;
        else return b;
    }
};

需要特别注意外联函数在类体外创建对象/函数的语法:

在类体外创建对象/函数

基本格式:类名<类型> 对象名;
其中:

  • 类名即为你定义的类的名字
  • 类型可以为:
    • 抽象类型
    • 具体类型,此时相当于将具体类型代入模版

由于声明语句template <class T>只对它后面紧挨着的函数/类有效,也就是说,紧挨着的类体/函数结束后,程序就不知道T是什么了。所以在类体外创建对象/函数的时候,我们有两种选择:

1.再次声明抽象类型

template <class T> //名字不一定非要是T
point<T> t; 

2.代入具体类型

point<int> t; //将抽象类型具像化为整型int

事实上,后面会学到STL中的几种“自带”数据结构:先进后出的栈stack、先进先出的队列quque等,它们的定义格式是这样的:

#include <stack> //栈的头文件
#include <queue> //队列的头文件
using namespace std;
stack<int> s; //定义一个名为s的栈
queue<char> q; //定义一个名为q的队列

如果你恰好懂模版,你就会真正理解,这两句定义是什么意思。你甚至可以不写头文件,自己编写相关成员函数,实现它的功能。

外联函数

外联函数在类体外有一个实现的过程:

class point{
private:
    T a,b;
public:
    point(T A, T B){
        a=A, b=B;
    }
    void Swap(); //外联函数,类体内声明
};

//类体外实现:
template<class T> 
void point<T>::Swap() { 
    T tmp;
    tmp=a;
    a=b;
    b=tmp;
}

猜你喜欢

转载自blog.csdn.net/pyx2466079565/article/details/108418755