C++ Template

引言

模板(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\n", num1, num2);  return 0;
}
复制代码

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


类模板

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

复制代码
//statck.h
template <class T> class Stack {public:Stack();~Stack();void push(T t);T pop();bool isEmpty();private:T *m_pT;        int m_maxSize;int m_size;
};#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,代码如下:

复制代码
//statck.h
template <class T,int maxsize = 100> class Stack {public:Stack();~Stack();void push(T t);T pop();bool isEmpty();private:T *m_pT;        int m_maxSize;int m_size;
};#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<int>,在使用原来的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<int>的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<char *>  
{
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代码如下:

复制代码
//statck.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<Circle>或者Stack<Circle*>就可以自动转换为Stack<Shape>或者Stack<Shape*>,如果转换的类型是Stack<int>到Stack<Shape>,编译器会报错。

其他

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

复制代码
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>(1, 2);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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/462136.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Oracle--plsql异常处理

•什么是异常?Oracle中出现错误的情形通常分为编译时错误&#xff08;compile-timeerror&#xff09;和运行时错误(run-time error)&#xff0c;异常是在PL/SQL执行过程中出现的警告或错误。•异常是如何触发的?–发生了一个 Oracle 错误时–使用RAISE语句显式触发•如何处理…

再谈SQL Server中日志的的作用

简介 之前我已经写了一个关于SQL Server日志的简单系列文章。本篇文章会进一步挖掘日志背后的一些概念&#xff0c;原理以及作用。如果您没有看过我之前的文章&#xff0c;请参阅&#xff1a; 浅谈SQL Server中的事务日志(一)----事务日志的物理和逻辑构架 浅谈SQL Server中的事…

VS2012和XE2013的关联和设置问题

1. 首先要正确安装Microsoft VisualStudio 2012 Intel Parallel Studio XE 2013&#xff0c;其中&#xff0c;先安装VS2012&#xff0c;再安装XE2013。 2.Microsoft Visual Studio 2012 Intel Parallel Studio XE 2013集成检验&#xff1a; 打开&#xff1a;开始所有程序-&…

推荐一款移动端的web UI控件 -- mobiscroll

用mobiscroll 可实现ios系统自带的选择器控件效果&#xff0c;支持几乎所有的移动平台(iOS, Android, BlackBerry, Windows Phone 8, Amazon Kindle)&#xff0c;当然在pc的浏览器上跑&#xff0c;效果也还不错。建议使用支持css3的浏览器访问^_^ 支持换肤&#xff0c;效果和性…

I2C通信 读写数据过程

在通信之初&#xff0c;主从机必须根据自己的要求约定好通信规则&#xff1a;command的定义和位置、address的位数和位置。 以读写从机寄存器数据为例&#xff1a; 假设从机寄存器地址为8位、从机寄存器也位8位&#xff08;被读取数据为8位&#xff09;&#xff1b; 约定读comm…

C++的运算符重载(转)

C的运算符重载 cc扩展语言 C中预定义的运算符的操作对象只能是基本数据类型。但实际上&#xff0c;对于许多用户自定义类型&#xff08;例如类&#xff09;&#xff0c;也需要类似的运算操作。这时就必须在C中重新定义这些运算符&#xff0c;赋予已有运算符新的功能&#xff0c…

Android应用程序结构总结

2019独角兽企业重金招聘Python工程师标准>>> Android应用程序结构分析 由于是初学者&#xff0c;对于Android应用程序的结构的认识是一穷二白的&#xff0c;对于开发Android应用程序&#xff0c;必须先了解其程序的结构和作用。一下就用一个简单的例子来解剖&#…

递归--整数划分问题

问题描述&#xff1a; 将正整数n表示成一系列正整数之和&#xff1a;nn1n2…nk&#xff0c;其中n1≥n2≥…≥nk≥1&#xff0c;k≥1。正整数n的这种表示称为正整数n的划分。 问题1&#xff1a; 输出整数n的所有可能的划分&#xff0c;如&#xff1a; 输入&#xff1a;6 输出&am…

webrtc笔记(1): 基于coturn项目的stun/turn服务器搭建

webrtc是google推出的基于浏览器的实时语音-视频通讯架构。其典型的应用场景为&#xff1a;浏览器之间端到端(p2p)实时视频对话&#xff0c;但由于网络环境的复杂性(比如&#xff1a;路由器/交换机/防火墙等&#xff09;&#xff0c;浏览器与浏览器很多时候无法建立p2p连接&…

从0-1背包问题学习回溯法、分支界限法、动态规划

一、0-1背包问题的描述 下面将使用回溯法、分支界限法、动态规划法来分析和解决此问题。 二、回溯法 &#xff08;1&#xff09;算法步骤 &#xff08;2&#xff09;代码如下&#xff08;没有裁剪函数&#xff09;&#xff1a; 用i和n来判断结束与否&#xff0c;是因为解空间…

高可用性的HDFS—Hadoop分布式文件系统深度实践

《高可用性的HDFS—Hadoop分布式文件系统深度实践》基本信息作者&#xff1a; 文艾 王磊 出版社&#xff1a;清华大学出版社 ISBN&#xff1a;9787302282587上架时间&#xff1a;2012-5-14出版日期&#xff1a;2012 年5月开本&#xff1a;16开页码&#xff1a;371版次&#xff…

【作品】超级玛丽射击版

【下载地址】稍后上传 【以下内容摘自试验报告&#xff0c;可能狗屁不通&#xff0c;尽情谅解】 &#xff0d;游戏介绍: 简单的射击游戏,键盘控制动作[角色的移动,跳跃等],鼠标控制攻击的位置方向, 单击鼠标即射击,同时可以设置游戏规定时间,时间到则结束游戏. &#xff0d;游戏…

MySQL锁的用法之行级锁

2019独角兽企业重金招聘Python工程师标准>>> 行级锁是MySQL中粒度最小的一种锁&#xff0c;他能大大减少数据库操作的冲突。但是粒度越小&#xff0c;实现的成本也越高。MYISAM引擎只支持表级锁&#xff0c;而INNODB引擎能够支持行级锁&#xff0c;下面的内容也是针…

matlab中二维插值函数interp2的使用

下面是一段产生log-normal分布的代码&#xff0c;以此进行说明。 clear all; clc; for t1:100Traffic(t) curve(t); end MaxTraffic max(Traffic); w 0.2; Wmax 2*pi*w/3000; x[0:10:300]; y[0:10:300]; Nxlength(x); Nylength(y); Sigma 0.53; t 0&#xff1b; M 10*cu…

8天玩转并行开发——第五天 同步机制(下)

承接上一篇&#xff0c;我们继续说下.net4.0中的同步机制&#xff0c;是的&#xff0c;当出现了并行计算的时候&#xff0c;轻量级别的同步机制应运而生&#xff0c;在信号量这一块 出现了一系列的轻量级&#xff0c;今天继续介绍下面的3个信号量 CountdownEvent&#xff0c;Se…

Quartz 2D编程笔记

当我们需要在一个图形上下文中构建一个路径时&#xff0c;我们需要调用CGContextBeginPath来标记Quartz。然后&#xff0c;我们调用函数CGContextMovePoint来设置每一个图形或子路径的起始点。在构建起始点后&#xff0c;我们可以添加直线、弧、曲线。记住如下规则&#xff1a;…

个人管理 - 书籍推荐(待读)

在《个人管理 &#xff0d; 书籍推荐&#xff08;已读&#xff09;》中推荐了一些书籍&#xff0c;有些人从中选取了一些&#xff0c;但其实还有很多好书我还没有读&#xff0c;为了给大家更多选择&#xff0c;我把我欠下的书债也贴上来。由于豆瓣还没有提供书列表的功能&#…

Node.js用6行代码1个JS文件搭建一个HTTP静态服务器

2019独角兽企业重金招聘Python工程师标准>>> Node.js宣言&#xff1a;Node.js is a platform built on Chromes JavaScript runtime for easily building fast, scalable network applications. Node.js uses an event-driven, non-blocking I/O model that makes i…

ERROR: Start Page at 'www/index.html' was not found

用Xcode 4.3.2新建了一个PhoneGap的应用&#xff0c;www目录下存在index.html文件&#xff0c;但是运行的时候&#xff0c;报错&#xff1a;ERROR: Start Page at www/index.html was not found&#xff0c;这是PhoneGap和Xcode 4还不兼容导致的。 可以右键项目名->Add File…