模板的作用
模板是泛型编程的基础,泛型编程即以一种独立于任何特定类型的方式编写代码。
模板是创建泛型类或函数的蓝图或公式。库容器,比如迭代器和算法,都是泛型编程的例子,它们都使用了模板的概念。
- 模板分为函数模板和类模板,函数模版是用来生成函数的实例,类模版是用来生成类的实例。
- 一个模版就是一个类或函数的蓝图或者说是公式。当我们调用template时,编译器会使用实参的类型来确定绑定到模版参数T上的类型,之后编译器利用推断出的模版参数来实例化一个特定版本的函数,这个过程被称之为实例化。
- 编译器遇到一个模版的定义时,并不会产生代码,只有当我们实例化出模版的一个特定版本的时候,编译器才会产生代码。编译器遇到一个模版的定义时,并不会产生代码,只有当我们实例化出模版的一个特定版本的时候,编译器才会产生代码。
- 需要保证传递给模版的实参支持模版的所有操作(例如传入的是addr类型,则在类模板中操作"<“运算符时,addr类型需要实现重载这个运算符),以及这些操作在模版中正确工作。需要保证传递给模版的实参支持模版的所有操作(例如传入的是addr类型,则在类模板中操作”<"运算符时,addr类型需要实现重载这个运算符),以及这些操作在模版中正确工作。
函数模板:一个函数模版就是一个公式,可用来生成指定类型的函数版本,例如需要编写一个函数比较两个值,如果没有模板,则需要为各种类型(例如int,double,string等)各自编写一个比较函数。
类模板:类模板是用来生成类的模型,和函数模板不同的是,编译器不能为类模板推断模板参数类型,必须提供实参列表。这个是显然的,因为函数是直接调用,传入的参数可以推断出类型,但是类需要定义,如果不指定类型,则是不行的。
函数模板
类模板
参考这篇文章。
1. 基础的模板类
类模板,可以定义相同的操作,拥有不同数据类型的成员属性。
通常使用template
来声明。告诉编译器,碰到T
不要报错,表示一种泛型。
如下,声明一个普通的类模板:
template <class T>
class Stack {
private:
vector<T> elems; // 元素
public:
void push(T const&); // 入栈
void pop(); // 出栈
T top() const; // 返回栈顶元素
bool empty() const{ // 如果为空则返回真。
return elems.empty();
}
};
// 如果在类外定义成员函数,若此成员函数中有模板参数存在,
// 则除了需要和一般类的类外定义成员函数一样的定义外,
// 还需要在函数外进行模板声明
template <class T>
void Stack<T>::push (T const& elem) { }
template <class T>
void Stack<T>::pop () { }
template <class T>
T Stack<T>::top () const { }
int main()
{
//对象的定义,必须声明模板类型,因为要分配内容
Stack<int> a;
Stack<int> b;
return 0;
}
2 模板类的继承
在模板类的继承中,需要注意以下几点:
- 如果父类自定义了构造函数,记得子类要使用构造函数列表来初始化
- 继承的时候,如果子类不是模板类,则必须指明当前的父类的类型,因为要分配内存空间
- 继承的时候,如果子类是模板类,要么指定父类的类型,要么用子类的泛型来指定父类
template <typename T>
class Parent{
public:
Parent(T p)
{
this->p = p;
}
private:
T p;
};
//如果子类不是模板类,需要指明父类的具体类型
class ChildOne:public Parent<int>{
public:
ChildOne(int a,int b):Parent(b)
{
this->cone = a;
}
private:
int cone;
};
//如果子类是模板类,可以用子类的泛型来表示父类
template <typename T>
class ChildTwo:public Parent<T>{
public:
ChildTwo(T a, T b):Parent<T>(b)
{
this->ctwo = a;
}
private:
T ctwo;
};