文章目录

  • 1.类模板
  • 2.非类型模板参数

1.类模板

  • 类模板:将类定义中的数据类型参数化,用参数来传递
    eg:vector,表示vector内部所存放的数据类型是int,以此类推,vector
  • 类模板实际上是函数模板的推广,可以用相同的类模板来组建任意类型的对象集合
    eg:vector,int类型对象的集合vector,vector,字符串类型的集合string
  • 类模板的定义
    直接用代码说话!
template  <类型形参表>
class  <类名>
{     //类说明体  };
template  <类型形参表>
<返回类型> <类名> <类型名表>::<成员函数1>(形参表)
{     //成员函数定义体  }
template  <类型形参表>
<返回类型> <类名> <类型名表>::<成员函数2>(形参表)
{     //成员函数定义体  }
…
template  <类型形参表>
<返回类型> <类名> <类型名表>::<成员函数n>(形参表)
{     //成员函数定义体  }
  • 使用类模板
    (1)类模板的实例化:用具体的数据类型替换模板的参数以得到具体的类(模板类)
    (2)模板类也可以实例化为对象
    (3)用下列方式创建类模板的实例:
类名 <类型实参表>  对象名称;

(P69)模板二:类模板,非类型模板参数_c++


类模板不是类,首先要实例化出模板类,好像是函数模板,它不是函数,要先实例化出模板函数出来,才能出来。

与函数模板不同,类模板只能显式实例化为模板类,函数模板可以根据所传送的数据类型不同来自动推导,而类模板只能显式实例化

  • eg:用连续的内存空间实现栈,并以模板的实现提供,这样栈可以存放任意类型
    P69\Stack.h
#ifndef _STACK_H
#define _STACK_H

#include <exception>

//Stack是一个模板,并不是一个类
//typename T:将类型作为参数来传递,typename T是类型参数
template <typename T>
class Stack
{
public:
    //分配一个T类型的连续内存空间
    explicit Stack(int maxsize);
    ~Stack();
    void Push(const T& elem);
    void Pop();
    T& Top();//返回栈顶元素,不带const说明可以改变栈内部的数据
    const T& Top() const;//返回栈顶元素,带const说明不能改变栈内部的数据
    bool Empty() const;//说明栈是否是空的
private:
    T* elems_;
    int maxSize_;
    int top_;//当前的栈顶位置
};

//类的实现可以放在类说明的外部
//每个成员函数看成是一个函数模板
//Stack<T>表示模板,Stack是成员函数
template <typename T>
Stack<T>::Stack(int maxsize) : maxSize_(maxsize), top_(-1)
{
    elems_ = new T[maxsize];
}

template <typename T>
Stack<T>::~Stack()
{
    delete[] elems_;
}

template <typename T>
void Stack<T>::Push(const T& elem)
{
    //top+1才是是元素的个数
    if (top +1 >= maxSize_)
        throw out_of_range(“Stack<>::Push() stack full”);//out_of_range是标准库的异常
    elems_[top_++] = elem;
}


template <typename T>
void Stack<T>::Pop(const T& elem)
{
    //top+1才是是元素的个数
    if (top +1 == 0)
        throw out_of_range(“Stack<>::Pop() stack empty”);//out_of_range是标准库的异常

    --top_;
}

template <typename T>
T& Stack<T>::Top()
{
    //top+1才是是元素的个数
    if (top +1 == 0)
        throw out_of_range(“Stack<>::Top() stack empty”);//out_of_range是标准库的异常

    return elems_[top_];
}

template <typename T>
const T& Stack<T>::Top() const
{
    //top+1才是是元素的个数
    if (top +1 == 0)
        throw out_of_range(“Stack<>::Top() const stack empty”);//out_of_range是标准库的异常

    return elems_[top_];
}

template <typename T>
bool Stack<T>::Empty() const
{
    return top_ + 1 == 0;
}

#endif //_STACK_H

P69\01.cpp

#include "Stack.h"


int main(void)
{
    //显示的实例化一个int类型的模板类,而不是自动推导的
    //Stack是一个模板,Stack<int>是一个模板类
    Stack<int> s(10);//当我们使用时,Stack<int>进行实例化,是在编译器完成的,会将头文件里面的template
                    //那些东西用int类型来替换,模板实际上是宏和重载的结合,只是说重载的版本是编译器维护的,而不是程序员
    s.Push(1);
    s.Push(2);
    s.Push(3);

    while (!s.Empty())
    {
        cout<<s.Top()<<endl;
        s.Pop();
    }

    return 0;
}
  • 测试:

2.非类型模板参数

  • 对于函数模板与类模板,模板参数并不局限于类型,普通值也可以作为模板参数
    将值作为模板的参数也是可以的
  • eg:
    P69\Stack2.h
#ifndef _STACK2_H
#define _STACK2_H

#include <exception>

//Stack2是一个模板,并不是一个类
//typename T:将类型作为参数来传递,typename T是类型参数,int MAX_SIZE是非类型参数

template <typename T, int MAX_SIZE>
class Stack2
{
public:
    //分配一个T类型的连续内存空间
    Stack2();
    ~Stack2();
    void Push(const T& elem);
    void Pop();
    T& Top();//返回栈顶元素,不带const说明可以改变栈内部的数据
    const T& Top() const;//返回栈顶元素,带const说明不能改变栈内部的数据
    bool Empty() const;//说明栈是否是空的
private:
    T* elems_;
    // int maxSize_;
    int top_;//当前的栈顶位置
};

//类的实现可以放在类说明的外部
//每个成员函数看成是一个函数模板
//Stack2<T, int MAX_SIZE>表示模板,Stack2是成员函数
template <typename T, int MAX_SIZE>
Stack2<T, int MAX_SIZE>::Stack2() : top_(-1)
{
    elems_ = new T[MAX_SIZE];
}

template <typename T, int MAX_SIZE>
Stack2<T, int MAX_SIZE>::~Stack2()
{
    delete[] elems_;
}

template <typename T, int MAX_SIZE>
void Stack2<T, int MAX_SIZE>::Push(const T& elem)
{
    //top+1才是是元素的个数
    if (top +1 >= MAX_SIZE)
        throw out_of_range(“Stack2<>::Push() stack full”);//out_of_range是标准库的异常
    elems_[top_++] = elem;
}


template <typename T, int MAX_SIZE>
void Stack2<T, int MAX_SIZE>::Pop(const T& elem)
{
    //top+1才是是元素的个数
    if (top +1 == 0)
        throw out_of_range(“Stack2<>::Pop() stack empty”);//out_of_range是标准库的异常

    --top_;
}

template <typename T, int MAX_SIZE>
T& Stack2<T, int MAX_SIZE>::Top()
{
    //top+1才是是元素的个数
    if (top +1 == 0)
        throw out_of_range(“Stack2<>::Top() stack empty”);//out_of_range是标准库的异常

    return elems_[top_];
}

template <typename T, int MAX_SIZE>
const T& Stack2<T, int MAX_SIZE>::Top() const
{
    //top+1才是是元素的个数
    if (top +1 == 0)
        throw out_of_range(“Stack2<>::Top() const stack empty”);//out_of_range是标准库的异常

    return elems_[top_];
}

template <typename T, int MAX_SIZE>
bool Stack2<T, int MAX_SIZE>::Empty() const
{
    return top_ + 1 == 0;
}

#endif //_STACK2_H

P69\02.cpp

#include "Stack2.h"


int main(void)
{
    //显示的实例化一个int类型的模板类,而不是自动推导的
    //Stack是一个模板,Stack<int>是一个模板类
    Stack2<int, 10> s;//当我们使用时,Stack<int>进行实例化,是在编译器完成的,会将头文件里面的template
                    //那些东西用int类型来替换,模板实际上是宏和重载的结合,只是说重载的版本是编译器维护的,而不是程序员
    s.Push(1);
    s.Push(2);
    s.Push(3);

    while (!s.Empty())
    {
        cout<<s.Top()<<endl;
        s.Pop();
    }

    return 0;
}
  • 测试:
  • (P69)模板二:类模板,非类型模板参数_开发语言_02

  • 进一步:C++非类型模板参数