引言

模板(template)指C++程序设计语言中采用类型作为参数的程序设计,支持通用程序设计。C++的标准库提供许多有用的函数大多结合了模板的概念,如STL 以及 IO Stream。

函数模板

在C++入门中,很多人会接触swap(int&, int&) 这样的函数,类似代码如下:

void swap(int&a , int& b) {int temp = a;a =  b;b = temp;
}

但是如果是要支持long,string,自定义class的swap函数,代码和上述代码差不多,只是类型不同,这个时候就是我们定义swap 的函数模板,就可以复用不同类型的swap函数代码,函数模板的声明形式如下:

template <class identifier> function_declaration;
template <typename identifier> function_declaration;

swap函数模板的声明和定义代码如下:

//method.h
template <typename T> void swap(T& t1, T& t2);#include "method.cpp"
// method.cpp
template <typename T>
void swap(T &t1, T &t2){T tmpT; tmpT = t1; t1 = t2;t2 = tmpT;
}

上述是模板的声明和定义了,那模板如何实例化呢,模板的实例化是编译器做的事情,与程序员无关,那么上述模板如何使用呢,代码如下:

// main.cpp
#include <stdio.h>
#include "method.h"int main(){// 模板方法int num1 = 1, num2 = 2;swap<int>(num1, num2);printf("num1:%d, num2:%d", num1, num2);return 0;
}

这里使用 swap 函数,必须包含swap的定义,否则编译会出错,这个和一般的函数使用不一样。所以必须在 method.h 文件的最后一行加入 “#include <method.cpp>”。

类模板

考虑我们写一个简单的栈的类,这个栈可以支持int类型,long类型,string类型等,不利用类模板,我们就要写三个以上的stack类,其中代码基本一样,通过类模板,我们可以定义一个简单的栈模板,再根据需要实例化为int栈,long栈,string栈。

// stack.h
template <class T>
class Stack{private: T *m_pT; int m_maxSize;int m_size;public:Stack();~Stack();void push(T t);T pop();bool isEmpty();
}
#include "stack.cpp"
// stack.cpp
template <class T>
Stack<T>::Stack(){m_maxSize = 100;m_size = 0;m_pT = new T[m_maxSize];
}template <class T>
Stack<T>::~Stack(){delete []m_pT;
}template <class T>
void Stack<T>::push(T t){m_size ++;m_pT[m_size - 1] = t;
}template <class T>
T Stack<T>::pop(){T t = m_pT[m_size - 1];m_size --;return t;
}template <class T>
bool Stack<T>::isEmpty(){return m_size == 0;
}

上述定义了一个类模板–栈,这个栈很简单,只是为了说明类模板如何使用而已,最多只能支持100个元素入栈,使用示例如下:

// main.cpp
#include <stdio.h>
#include "stack.h"int main(){Stack<int> intStack;intStack.push(1);intStack.push(2);intStack.push(3);while(! intStack.isEmpty()){printf("num:%d\n", intStack.pop());}return 0;
}

模板参数

模板可以有类型参数,也可以有常规的类型参数int,也可以有默认模板参数,例如

template <class T, T def_val> class Stack{...}

上述类模板的栈有一个限制,就是最多只能支持100个元素,我们可以使用模板参数配置这个栈的最大元素数,如果不配置,就设置默认最大值为100,代码如下:

// stack.h
template <class T, int maxsize = 100> class Stack{private:T *m_pT;int m_maxSize;int m_size;public:Stack();~Stack();void push(T t);T pop();bool isEmpty();
};#include "stack.cpp"
// stack.cpp
template <class T, int maxsize>
Stack<T, maxsize>::Stack(){m_maxSize = maxsize;m_size = 0;m_pT = new T[m_maxsize];
}template <class T, int maxsize>
Stack<T, maxsize>::~Stack(){delete []m_pT;
}template <class T, int maxsize>
void Stack<T, maxsize>::push(T t){m_size ++;m_pT[m_size - 1] = t;
}template <class T, int maxsize>
T Stack<T, maxsize>::pop(){T t = m_pT[m_size - 1];m_size --;return t;
}template <class T, int maxsize>
bool Stack<T, maxsize>::isEmpty(){return m_size == 0;
}

使用示例如下:

// main.cpp
#include <stdio.h>
#include "stack.h"
int main(){int maxsize = 1024;Stack<int, 1024> intStack;for(int i=0; i<maxsize; i++){intStack.push(i);}while(!intStack.isEmpty()){printf("num:%d\n", intStack.pop());}return 0;
}

模板专门化

当我们要定义模板的不同实现,我们可以使用模板的专门化。例如我们定义的stack类模板,如果是char*类型的栈,我们希望可以复制char的所有数据到stack类中,因为只是保存char指针,char指针指向的内存有可能会失效,stack弹出的堆栈元素char指针,指向的内存可能已经无效了。还有我们定义的swap函数模板,在vector或者list等容器类型时,如果容器保存的对象很大,会占用大量内存,性能下降,因为要产生一个临时的大对象保存a,这些都需要模板的专门化才能解决。

函数模板专门化

假设我们swap函数要处理一个情况,我们有两个很多元素的vector,在使用原来的swap函数,执行tmpT = t1要拷贝t1的全部元素,占用大量内存,造成性能下降,于是我们系统通过vector.swap函数解决这个问题,代码如下:

// method.h
template <class T> void swap(T &t1, T &t2);
#include "method.cpp"
#include <vector>
using namespace std;template <class T>
void swap(T &t1, T &t2){T tmpT;tmpT = t1;t1 = t2;t2 = tmpT;
}template <>
void swap(std::vector<int>& t1, std::vector<int>& t2){t1.swap(t2);
}

template<>前缀表示这是一个专门化,描述时不用模板参数,使用示例如下:

// main.cpp
#include <stdio.h>
#include <vector>
#include <string>
#include "method.h"int main(){using namespace std;// 模板方法string str1 = "1", str2 = "2";swap(str1, str2);printf("str1:%s, str2:%s\n", str1.c_str(), str2.c_str());vector<int> v1, v2;v1.push_back(1);v2.push_back(2);swap(v1, v2);for(int i=0; i<v1.size(); i++){printf("v1[%d]: %d\n", i, v1[i]);}for(int i=0; i<v2.size(); i++){printf("v2[%d]: %d\n", i, v2[i]);}return 0;
}

vector的swap代码还是比较局限,如果要用模板专门化解决所有vector的swap,该如何做呢,只需要把下面代码

template <>
void swap(std::vector<int>& t1, std::vector<int>& t2){t1.swap(t2);
}

改为

template <class V>
void swap(std::vector<V>& t1, std::vector<V>& t2){t1.swap(t2);
}

就可以了,其他代码不变。

类模板专门化,请看下面的 compare 代码:

//compare.h
template <class T>
class compare{public:bool equal(T t1, T t2){return t1==t2;}
}
#include <iostream>
#include "compare.h"int main(){using namespace std;char str1[] = "Hello";char str2[] = "Hello";compare<int> c1;compare<char *> c2;cout << c1.equal(1, 1) << endl; // 比较两个int类型的参数cout << c2.equal(str1, str2) << endl; // 比较两个char *类型的参数return 0;
}

在比较两个整数,compare的equal方法是正确的,但是compare的模板参数是char*时,这个模板就不能工作了,于是修改如下:

// compare.h
#include <string.h>template <class T>
class compare{public:bool equal(T t1, T t2){return t1==t2;}
};template <>
class compare{public:bool equal(char* t1, char* t2){return strcmp(t1, t2) == 0;       }
}

main.cpp文件不变,此代码可以正常工作。

模板类型转换

还记得我们自定义的Stack模板吗,在我们的程序中,假设我们定义了Shape和Circle类,代码如下:

// shape.h
class Shape{};
class Circle : public Shape{};

然后我们希望可以这么使用:

// main.cpp
#include <stdio.h>
#include "stack.h"
#include "shape.h"int main(){Stack<Circle*> pcircleStack;Stack<Shape*> pshapeStack;pcircleStack.push(new Circle);pshapeStack = pcircleStack;return 0;
}

这里是无法编译的,因为Stack<Shape*>不是Stack<Circle*>的父类,然而我们却希望代码可以这么工作,那我们就要定义转换运算符了,Stack代码如下:

// stack.h
template <class T>
class Stack{public:Stack();~Stack();void push(T t);T pop();bool isEmpty();template <class T2> operator Stack<T2>();private:T *m_pT;int m_maxSize;int m_size;
};
#include "stack.cpp"
template <class T>
Stack<T>::Stack(){m_maxSize = 100;m_size = 0;m_pT = new T[m_maxSize];
}template <class T>
Stack<T>::~Stack(){delete[] m_pT;
}template <class T>
void Stack<T>::push(T t){m_size ++;m_pT[m_size - 1] = t;
}template <class T>
T Stack<T>::pop(){T t = m_pT[m_size - 1];m_size --;return t;
}template <class T>
bool Stack<T>::isEmpty(){return m_size == 0;
}template <class T>
template <class T2> Stack<T>::operator Stack<T2>(){Stack<T2> StackT2;for(int i=0; i<m_size; i++){StackT2.push((T2)m_pT[m_size - 1]);}return StackT2;
}
// main.cpp
#include <stdio.h>
#include "stack.h"
#include "shape.h"int main(){Stack<Circle*> pcircleStack;Stack<Shape*> pshapeStack;pcircleStack.push(new Circle);pshapeStack = pcircleStack;return 0;
}

这样,Stack或者Stack<Circle*>就可以自动转换为Stack或者Stack<Shape*>,如果转换的类型是Stack到Stack,编译器会报错。

其他

一个类没有模板参数,但是成员函数有模板参数,是可行的,代码如下:

class Util{public:template <class T> bool equal(T t1, T t2){return t1==t2;}
};int main(){Util util;int a=1, b=2;util.equal<int>(a, b);return 0;
}

甚至可以把Util的equal声明为static,代码如下:

class Util{public:template <class T> static bool equal(T t1, T t2){return t1==t2;}
};int main(){int a=1, b=2;Util::equal<int>(1, 2);return 0;
}

C++ Template用法总结相关推荐

  1. Redis简介和Redis Template用法整理

    Redis简介 Redis使大规模互联网应用常用的内存高速缓存数据库,它的读写速度非常快.Redis是目前使用最广泛的内存数据存储系统之一.它支持更丰富的数据结构,支持数据持久化.事务.HA(高可用H ...

  2. python template用法_python中Template的使用介绍

    Template无疑是一个好东西,可以将字符串的格式固定下来,重复利用.同时Template也可以让开发人员可以分别考虑字符串的格式和其内容了,无形中减轻了开发人员的压力. Template属于str ...

  3. text/template用法

    1. 模板标签 模板标签用"{{"和"}}"括起来 2. 注释 # 使用"{{/*"和"*/}}"来包含注释内容 {{/ ...

  4. Go语言学习笔记【18】 Go语言常见库:html/template

    [声明] 非完全原创,部分内容来自于学习其他人的理论和B站视频.如果有侵权,请联系我,可以立即删除掉. 一.html/template 主要参考文档: (1)Go语言标准库之http/template ...

  5. vue 监控元素宽度_Vue入门系列之Vue实例详解与生命周期

    Vue的实例是Vue框架的入口,其实也就是前端的ViewModel,它包含了页面中的业务逻辑处理.数据模型等,当然它也有自己的一系列的生命周期的事件钩子,辅助我们进行对整个Vue实例生成.编译.挂着. ...

  6. ELK学习5_ELK文档资料:《ELK stack 权威指南/饶琛琳》推荐

    <ELK stack 权威指南/饶琛琳>的内容在网上共享有电子版,网站地址是: http://kibana.logstash.es/ . 作者也提供了电子版的下载,下载描述如下: You ...

  7. vue devtools面板没有显示_vue 基础入门(四)

    vue 基础入门(四) 1.全局配置 Vue.config 是一个对象,包含 Vue 的全局配置.可以在启动应用之前修改下列 property: 1.1 silent 类型:boolean 默认值:f ...

  8. 05Vue.js快速入门-Vue实例详解与生命周期

    Vue的实例是Vue框架的入口,其实也就是前端的ViewModel,它包含了页面中的业务逻辑处理.数据模型等,当然它也有自己的一系列的生命周期的事件钩子,辅助我们进行对整个Vue实例生成.编译.挂着. ...

  9. 2014/08/11 – Backbonejs

    2014/08/11 – Backbonejs [来自: Backbone.js 开发秘笈 第6章] Template 用法: 通过 Underscore.js 提供的 _.template() 方法 ...

  10. Vue详细介绍及使用

    Vue详细介绍及使用 一.Vue定义及简介 1.Vue定义 关于Vue简介,百度百科给出的解释是:Vue.js是一套构建用户界面的渐进式框架.与其他重量级框架不同的是,Vue 采用自底向上增量开发的设 ...

最新文章

  1. mysql 获取游标索引_MYSQL视图 事务 游标 索引及其使用
  2. nginx源码分析—内存池结构ngx_pool_t及内存管理(精辟)
  3. xml getelementsbytagname php,用PHP编写和读取XML的几种方式
  4. 子类调用父类构造器的几种情况
  5. ubuntu linux设置网关,ubuntu修改静态IP网关
  6. 无论干什么事都要对自己有信心!
  7. Java — Map.keySet()、Map.put()、Map.get()【Map类、Set类】
  8. eplan2.5安装教程
  9. 从无到有 win10建window xp虚拟机之总结
  10. 西门子s7-200解密软件下载_西门子S7200plc软件仿真软件使用方法
  11. php theexcerpt,WordPress:使用the_excerpt函数显示摘要信息
  12. TCP/IP协议第一章笔记
  13. 2 snippets vue 修改配置_自动化配置vscode的snippets
  14. (附源码 )SSM+mysql+微信小程序网易云音乐设计与实现 毕业设计261620
  15. 深度学习入门书籍和资源
  16. word论文页码的设置(封面无编号、目录罗马数字和正文阿拉伯数字)
  17. 史上最全!!!ARM架构下的NVIDIA Xavier安装ROS-Melodic以及使用速腾激光雷达+A-loam获取点云图
  18. Mediacodec 如何硬件解码到纹理的
  19. 收银小票编码生成java_Java练习题——打印购物小票
  20. Android消息推送

热门文章

  1. 开源H5盲盒商城源码系统4.0-vue+TP5php框架开发开源网站
  2. 调查称82%网友面对安全问题无从下手
  3. 浅谈内存分配方式以及堆和栈的区别
  4. matlab高尔顿板钉试验,高尔顿钉板试验动态图形软件的设计与制作
  5. 自动执行bash被拒绝 提示/bin/sh: /test.sh: Permission denied
  6. npm报错, Error: EPERM: operation not permitted, mkdir
  7. 风险预测模型_利用好预后预测模型,2个月发篇4分+SCI不是梦
  8. 学习Java,可以从事哪些岗位?
  9. SpringBoot上传文件报The field file exceeds its maximum permitted size of 1048576 bytes.
  10. matlab两矩阵相似性,两个矩阵同时相似对角化MATLAB程序.docx