在编写含有模板的程序的时候,我还是按照一个头文件声明,一个源文件的方法来组织,结果编译的时候总出现一些很奇怪的语法问题,但程序明明是没有问题的。后来经过查阅才知道原来是因为C++编译器不支持对模板的分离式编译,详细原因可参考博文为什么C++编译器不能支持对模板的分离式编译。所以,我在编写程序的时候,使用的是模板声明和实现放在同一个文件中的方法,即使用后缀为.hpp的文件(当然也可以是.h文件,但用.hpp文件更方便表明声明和实现放在一起)。

模板  

  模板是C++支持参数化多态的工具,使用模板可以使用户为类或者函数声明一种一般模式,使得类中的某些数据成员或者成员函数的参数、返回值取得任意类型。

  模板是一种对类型进行参数化的工具;

  通常有两种形式:函数模板类模板

  函数模板针对仅参数类型不同的函数

  类模板针对仅数据成员成员函数类型不同的类。

  使用模板的目的就是能够让程序员编写与类型无关的代码。比如编写了一个交换两个整型int 类型的swap函数,这个函数就只能实现int 型,对double,字符这些类型无法实现,要实现这些类型的交换就要重新编写另一个swap函数。使用模板的目的就是要让这程序的实现与类型无关,比如一个swap模板函数,即可以实现int 型,又可以实现double型的交换。模板可以应用于函数和类。下面分别介绍。

  注意:模板的声明或定义只能在全局,命名空间或类范围内进行。即不能在局部范围,函数内进行,比如不能在main函数中声明或定义一个模板。

函数模板   

函数模板的格式

template <class 形参名,class 形参名,......> 返回类型 函数名(参数列表)
{
函数体
}

  其中templateclass是关键字,class可以用typename 关键字代替,在这里typename 和class没区别,<>括号中的参数叫模板形参,模板形参和函数形参很相像,模板形参不能为空。一但声明了模板函数就可以用模板函数的形参名声明类中的成员变量和成员函数,即可以在该函数中使用内置类型的地方都可以使用模板形参名。模板形参需要调用该模板函数时提供的模板实参来初始化模板形参,一旦编译器确定了实际的模板实参类型就称他实例化了函数模板的一个实例。比如swap的模板函数形式为

template <class T> void swap(T& a, T& b)
{
}

  当调用这样的模板函数时类型T就会被被调用时的类型所代替,比如swap(a,b)其中abint 型,这时模板函数swap中的形参T就会被int 所代替,模板函数变为swap(int &a, int &b)。而当swap(c,d)其中cddouble类型时,模板函数会被替换为swap(double &a, double &b),这样就实现了函数的实现与类型无关的代码。

  注:关于class与typename区别(摘自stackoverflow,详细可参考Stan Lippman的一篇博客

  Stroustrup originally used class to specify types in templates to avoid introducing a new keyword. Some in the committee worried that this overloading of the keyword led to confusion. Later, the committee introduced a new keyword typename to resolve syntactic ambiguity, and decided to let it also be used to specify template types to reduce confusion, but for backward compatibility, class kept its overloaded meaning.

注意

  对于函数模板而言不存在 h(int, int) 这样的调用,不能在函数调用的参数中指定模板形参的类型,对函数模板的调用应使用实参推演来进行,即只能进行 h(2,3) 这样的调用,或者int a, b; h(a,b)

示例

 // funcTemp.hpp
template<typename T> void swapTest(T& t1, T& t2)
{
T tmpT;
tmpT = t1;
t1 = t2;
t2 = tmpT;
}

funcTemp.hpp

 // main.cpp
#include <iostream>
#include "funcTemp.hpp" using namespace std; int main()
{
int num1 = , num2 = ;
swapTest<int>(num1, num2);
cout << "num1: " << num1 << " num2: " << num2 << endl;
return ;
}

main.cpp

类模板  

类模板的格式

template<class  形参名,class 形参名,…>
class 类名
{
...
};

   类模板和函数模板都是以template开始后接模板形参列表组成,模板形参不能为空,一但声明了类模板就可以用类模板的形参名声明类中的成员变量和成员函数,即可以在类中使用内置类型的地方都可以使用模板形参名来声明。比如

 template<class T>
class A
{
public:
T a;
T b;
T hy(T c, T &d);
8 };

  在类A中声明了两个类型为T的成员变量ab,还声明了一个返回类型为T带两个参数类型为T的函数hy

类模板对象的创建

  比如一个模板类A,则使用类模板创建对象的方法为A<int> m;在类A后面跟上一个<>尖括号并在里面填上相应的类型,这样的话类A中凡是用到模板形参的地方都会被int 所代替。当类模板有两个模板形参时创建对象的方法为A<int, double> m;类型之间用逗号隔开。

对于类模板,模板形参的类型必须在类名后的尖括号中明确指定

  比如A<2> m;用这种方法把模板形参设置为int是错误的(编译错误:error C2079: 'a' uses undefined class 'A<int>'),类模板形参不存在实参推演的问题。也就是说不能把整型值2推演为int 型传递给模板形参。要把类模板形参调置为int 型必须这样指定A<int> m

在类模板外部定义成员函数的方法

template<模板形参列表> 函数返回类型 类名<模板形参名>::函数名(参数列表){ 函数体 }

  比如有两个模板形参T1T2的类A中含有一个void h()函数,则定义该函数的语法为:

template<class T1, class T2> void A<T1, T2>::h(){}

  注意:当在类外面定义类的成员时template后面的模板形参应与要定义的类的模板形参一致。

再次提醒注意

  模板的声明或定义只能在全局,命名空间或类范围内进行。即不能在局部范围,函数内进行,比如不能在main函数中声明或定义一个模板。

示例1:栈(大小预定)

 // stackTest.hpp
template<class T>
class stackTest
{
public:
stackTest();
~stackTest();
void push(T t);
T pop();
bool isEmpty(); private:
T *m_pT;
int m_maxsize;
int m_size; }; template<class T> stackTest<T>::stackTest()
{
m_maxsize = ;
m_size = ;
m_pT = new T[m_maxsize];
} template<class T> stackTest<T>::~stackTest()
{
delete[] m_pT;
} template<class T> void stackTest<T>::push(T t)
{
m_size++;
m_pT[m_size - ] = t;
} template<class T> T stackTest<T>::pop()
{
T t = m_pT[m_size - ];
m_size--;
return t;
} template<class T> bool stackTest<T>::isEmpty()
{
return m_size == ;
}

stackTest.hpp

 // main.cpp
#include <iostream>
#include"stackTest.hpp" using namespace std; int main()
{
stackTest<int> tmpStack;
tmpStack.push();
tmpStack.push();
tmpStack.push(); while (!tmpStack.isEmpty())
{
cout << tmpStack.pop() << endl;
} return ;
}

main

示例2:栈(大小可配置)

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

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

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

 // stackTest.h

 template<class T, int maxsize = >
class stackTest
{
public:
stackTest();
~stackTest();
void push(T t);
T pop();
bool isEmpty(); private:
T *m_pT;
int m_maxsize;
int m_size; }; template<class T, int maxsize> stackTest<T, maxsize>::stackTest()
{
m_maxsize = maxsize;
m_size = ;
m_pT = new T[m_maxsize];
} template<class T, int maxsize> stackTest<T, maxsize>::~stackTest()
{
delete[] m_pT;
} template<class T, int maxsize> void stackTest<T, maxsize>::push(T t)
{
m_size++;
m_pT[m_size - ] = t;
} template<class T, int maxsize> T stackTest<T, maxsize>::pop()
{
T t = m_pT[m_size - ];
m_size--;
return t;
} template<class T, int maxsize> bool stackTest<T, maxsize>::isEmpty()
{
return m_size == ;
}

stackTest.hpp

 // main.cpp
#include <iostream>
#include"stackTest.hpp" using namespace std; int main()
{
const int MAXSIZE = ;
stackTest<int, MAXSIZE> tmpStack; for (int i = ; i < MAXSIZE; i++)
{
tmpStack.push(i);
} while (!tmpStack.isEmpty())
{
cout << tmpStack.pop() << endl;
} return ;
}

main.cpp

参考资料

  C++ Template

  C++模板详解

  为什么C++编译器不能支持对模板的分离式编译  

 

C++模板总结的更多相关文章

  1. Jade模板引擎让你飞

    写在前面:现在jade改名成pug了 一.安装 npm install jade 二.基本使用 1.简单使用 p hello jade! 渲染后: <p>hello jade!</p ...

  2. ABP入门系列(2)——通过模板创建MAP版本项目

    一.从官网创建模板项目 进入官网下载模板项目 依次按下图选择: 输入验证码开始下载 下载提示: 二.启动项目 使用VS2015打开项目,还原Nuget包: 设置以Web结尾的项目,设置为启动项目: 打 ...

  3. CMS模板应用调研问卷

    截止目前,已经有数十家网站与我们合作,进行了MIP化改造,在搜索结果页也能看到"闪电标"的出现.除了改造方面的问题,MIP项目组被问到最多的就是:我用了wordpress,我用了织 ...

  4. PHP-自定义模板-学习笔记

    1.  开始 这几天,看了李炎恢老师的<PHP第二季度视频>中的“章节7:创建TPL自定义模板”,做一个学习笔记,通过绘制架构图.UML类图和思维导图,来对加深理解. 2.  整体架构图 ...

  5. 【原创分享·微信支付】C# MVC 微信支付之微信模板消息推送

    微信支付之微信模板消息推送                    今天我要跟大家分享的是“模板消息”的推送,这玩意呢,你说用途嘛,那还是真真的牛逼呐.原因在哪?就是因为它是依赖微信生存的呀,所以他能不 ...

  6. OpenCV模板匹配算法详解

    1 理论介绍 模板匹配是在一幅图像中寻找一个特定目标的方法之一,这种方法的原理非常简单,遍历图像中的每一个可能的位置,比较各处与模板是否“相似”,当相似度足够高时,就认为找到了我们的目标.OpenCV ...

  7. 前端MVC学习总结(一)——MVC概要与angular概要、模板与数据绑定

    一.前端MVC概要 1.1.库与框架的区别 框架是一个软件的半成品,在全局范围内给了大的约束.库是工具,在单点上给我们提供功能.框架是依赖库的.AngularJS是框架而jQuery则是库. 1.2. ...

  8. ThinkPHP+Smarty模板中截取包含中英文混合的字符串乱码的解决方案

    好几天没写博客了,其实有好多需要总结的,因为最近一直在忙着做项目,但是困惑了几天的Smarty模板中截取包含中英文混合的字符串乱码的问题,终于解决了,所以记录下来,需要的朋友看一下: 出现乱码的原因: ...

  9. ThinkPHP 模板substr的截取字符串函数

    ThinkPHP 模板substr的截取字符串函数在Common/function.php加上以下代码 /** ** 截取中文字符串 **/ function msubstr($str, $start ...

  10. DDD领域驱动设计 - 设计文档模板

    设计文档模板: 系统背景和定位 业务需求描述 系统用例图 关键业务流程图 领域语言整理,主要是整理领域中的各种术语的定义,名词解释 领域划分(分析出子域.核心域.支撑域) 每个子域的领域模型设计(实体 ...

随机推荐

  1. Android开发之Path类使用详解,自绘各种各样的图形!

    玩过自定义View的小伙伴都知道,在View的绘制过程中,有一个类叫做Path,Path可以帮助我们实现很多自定义形状的View,特别是配合xfermode属性来使用的时候.OK,那我们今天就来看看P ...

  2. [ExtJS5学习笔记]第三十六节 报表组件mzPivotGrid

    mzPivotGrid 是一个报表组件,采用这个组件之后,可以令你的应用体现更多的价值. 什么是pivot grid 什么是mzPivotGrid 学习资源 与图表组件的融合 什么是pivot gri ...

  3. 20 ViewPager Demo4自动轮播

    MainActivity.java 思想:才用非常大的数 让其看起来可以循环轮播图片并且用户可以从尽头滑到首图的特点 . package com.qf.day20_viewpager_demo4; i ...

  4. Python中使用rrdtool结合Django进行带宽监控

    我们有个网关需要做下带宽监控,能获取这个数据的唯一方法就是登录到管理界面查看.然后咱就写了个模拟登录的爬虫,定时抓取数据用rrdtool存储,最后通过Django来展示.这里就涉及了python的rr ...

  5. 带你深入理解STL之空间配置器(思维导图+源码)

    前不久把STL细看了一遍,由于看得太"认真",忘了做笔记,归纳和总结这步漏掉了.于是为了加深印象,打算重看一遍,并记录下来里面的一些实现细节.方便以后能较好的复习它. 以前在项目中 ...

  6. UNIX网络编程——解决TCP网络传输“粘包”问题

    当前在网络传输应用中,广泛采用的是TCP/IP通信协议及其标准的socket应用开发编程接口(API).TCP/IP传输层有两个并列的协议:TCP和UDP.其中TCP(transport contro ...

  7. App引导界面,可以这么玩

    什么是ViewPager,刚一听到这个词,我们可能感觉很奇怪,但是我相信我们大部分人都曾见到过这些界面的.其实它就是我们在安装好一个app之后第一次使用时的那些引导界面的效果.这就是通过ViewPag ...

  8. listview下拉刷新上拉加载扩展(三)-仿最新版美团外卖

    本篇是基于上篇listview下拉刷新上拉加载扩展(二)-仿美团外卖改造而来,主要调整了headview的布局,并加了两个背景动画,看似高大上,其实很简单: as源码地址:http://downloa ...

  9. Devstack: A copy of worked local.conf I'm sharing with you.

    service_plugins = neutron.services.firewall.fwaas_plugin.FirewallPlugin [service_providers] service_ ...

  10. 银联在线 网关支付 (JAVA版)

    这一版本的编写是在我上一次博客的基础上写的,有不懂得童鞋可以先看下我的原先在线支付的博客,熟悉下:http://blog.csdn.net/yulei_qq/article/details/45197 ...