1. 模板的概念。

我们已经学过重载(Overloading),对重载函数而言,C++的检查机制能通过函数参数的不同及所属类的不同。正确的调用重载函数。例如,为求两个数的最大值,我们定义MAX()函数需要对不同的数据类型分别定义不同重载(Overload)版本。

//函数1.

int max(int x,int y);
{return(x>y)?x:y ;}

//函数2.
float max( float x,float y){
return (x>y)? x:y ;}

//函数3.
double max(double x,double y)
{return (c>y)? x:y ;}

但如果在主函数中,我们分别定义了 char a,b; 那么在执行max(a,b);时 程序就会出错,因为我们没有定义char类型的重载版本。

现在,我们再重新审视上述的max()函数,它们都具有同样的功能,即求两个数的最大值,能否只写一套代码解决这个问题呢?这样就会避免因重载函数定义不 全面而带来的调用错误。为解决上述问题C++引入模板机制,模板定义:模板就是实现代码重用机制的一种工具,它可以实现类型参数化,即把类型定义为参数, 从而实现了真正的代码可重用性。模版可以分为两类,一个是函数模版,另外一个是类模版。

2.   函数模板的写法

函数模板的一般形式如下:

Template <class或者也可以用typename T>

返回类型 函数名(形参表)
{//函数定义体 }

说明: template是一个声明模板的关键字,表示声明一个模板关键字class不能省略,如果类型形参多余一个 ,每个形参前都要加class <类型 形参表>可以包含基本数据类型可以包含类类型.

请看以下程序:

//Test.cpp

#include <iostream>

using std::cout;

using std::endl;

//声明一个函数模版,用来比较输入的两个相同数据类型的参数的大小,class也可以被typename代替,

//T可以被任何字母或者数字代替。

template <class T>

T min(T x,T y)

{ return(x<y)?x:y;}

void main( )

{

int n1=2,n2=10;

double d1=1.5,d2=5.6;

cout<< "较小整数:"<<min(n1,n2)<<endl;

cout<< "较小实数:"<<min(d1,d2)<<endl;

system("PAUSE");

}

程序运行结果:

 

程序分析:main()函数中定义了两个整型变量n1 , n2 两个双精度类型变量d1 , d2然后调用min( n1, n2); 即实例化函数模板T min(T x, T y)其中T为int型,求出n1,n2中的最小值.同理调用min(d1,d2)时,求出d1,d2中的最小值.

3. 类模板的写法

定义一个类模板:

Template < class或者也可以用typename T >
class类名{
//类定义......
};

说明:其中,template是声明各模板的关键字,表示声明一个模板,模板参数可以是一个,也可以是多个。

例如:定义一个类模板:

// ClassTemplate.h
#ifndef ClassTemplate_HH

#define ClassTemplate_HH

template<typename T1,typename T2>

class myClass{

private:

T1 I;

T2 J;

public:

myClass(T1 a, T2 b);//Constructor

void show();

};

//这是构造函数

//注意这些格式

template <typename T1,typename T2>

myClass<T1,T2>::myClass(T1 a,T2 b):I(a),J(b){}

//这是void show();

template <typename T1,typename T2>

void myClass<T1,T2>::show()

{

cout<<"I="<<I<<", J="<<J<<endl;

}

#endif

// Test.cpp

#include <iostream>

#include "ClassTemplate.h"

using std::cout;

using std::endl;

void main()

{

myClass<int,int> class1(3,5);

class1.show();

myClass<int,char> class2(3,'a');

class2.show();

myClass<double,int> class3(2.9,10);

class3.show();

system("PAUSE");

}

最后结果显示:

4.非类型模版参数

一般来说,非类型模板参数可以是常整数(包括枚举)或者指向外部链接对象的指针。

那么就是说,浮点数是不行的,指向内部链接对象的指针是不行的。

template<typename T, int MAXSIZE>

class Stack{

Private:

T elems[MAXSIZE];

};

Int main()

{

Stack<int, 20> int20Stack;

Stack<int, 40> int40Stack;

};

5.使用模板类型

有时模板类型是一个容器或类,要使用该类型下的类型可以直接调用,以下是一个可打印STL中顺序和链的容器的模板函数

template <typename T>
void print(T v)
{
 T::iterator itor;
 for (itor = v.begin(); itor != v.end(); ++itor)
 {
  cout << *itor << " ";
 }
 cout << endl;
}

void main(int argc, char **argv){
 list<int> l;
 l.push_back(1);
 l.push_front(2);
 if(!l.empty())
  print(l);
 vector<int> vec;
 vec.push_back(1);
 vec.push_back(6);
 if(!vec.empty())
  print(vec);
}

打印结果

类型推导的隐式类型转换
在决定模板参数类型前,编译器执行下列隐式类型转换:

左值变换
  修饰字转换
  派生类到基类的转换

见《C++ Primer》([注2],P500)对此主题的完备讨论。

简而言之,编译器削弱了某些类型属性,例如我们例子中的引用类型的左值属性。举例来说,编译器用值类型实例化函数模板,而不是用相应的引用类型。

同样地,它用指针类型实例化函数模板,而不是相应的数组类型。

它去除const修饰,绝不会用const类型实例化函数模板,总是用相应的非 const类型,不过对于指针来说,指针和 const 指针是不同的类型。

底线是:自动模板参数推导包含类型转换,并且在编译器自动决定模板参数时某些类型属性将丢失。这些类型属性可以在使用显式函数模板参数申明时得以保留。

6. 模板的特化
如果我们打算给模板函数(类)的某个特定类型写一个函数,就需要用到模板的特化,比如我们打算用 long 类型调用 max 的时候,返回小的值(原谅我举了不恰当的例子):
template<> // 这代表了下面是一个模板函数
long max<long>( long a, long b ) // 对于 vc 来说,这里的 <long> 是可以省略的
{
  return a > b ? b : a;
}
实际上,所谓特化,就是代替编译器完成了对指定类型的特化工作,现代的模板库中,大量的使用了这个技巧。
对于偏特化,则只针对模板类型中部分类型进行特化,如

template<T1, T2>

class MyClass;

template<T1, T2>

class MyCalss<int, T2>//偏特化
7. 仿函数
仿函数这个词经常会出现在模板库里(比如 STL),那么什么是仿函数呢?
顾名思义:仿函数就是能像函数一样工作的东西,请原谅我用东西这样一个代词,下面我会慢慢解释。
void dosome( int i )
这个 dosome 是一个函数,我们可以这样来使用它: dosome(5);
那么,有什么东西可以像这样工作么?
答案1:重载了 () 操作符的对象,因此,这里需要明确两点:
  1 仿函数不是函数,它是个类;
  2 仿函数重载了()运算符,使得它的对你可以像函数那样子调用(代码的形式好像是在调用比如:
  struct DoSome
  {
  void operator()( int i );
  }
  DoSome dosome;
这里类(对 C++ 来说,struct 和类是相同的) 重载了 () 操作符,因此它的实例 dosome 可以这样用 dosome(5); 和上面的函数调用一模一样,不是么?所以 dosome 就是一个仿函数了。

实际上还有答案2:
  函数指针指向的对象。
  typedef void( *DoSomePtr )( int );
  typedef void( DoSome )( int );
  DoSomePtr *ptr=&func;
  DoSome& dosome=*ptr;
    
  dosome(5); // 这里又和函数调用一模一样了。
当然,答案3 成员函数指针指向的成员函数就是意料之中的答案了。

8. 仿函数的用处
不管是对象还是函数指针等等,它们都是可以被作为参数传递,或者被作为变量保存的。因此我们就可以把一个仿函数传递给一个函数,由这个函数根据需要来调用这个仿函数(有点类似回调)。
STL 模板库中,大量使用了这种技巧,来实现库的“灵活”。
比如:
for_each, 它的源代码大致如下:
template< typename Iterator, typename Functor >
void for_each( Iterator begin, Iterator end, Fucntor func )
{
  for( ; begin!=end; begin++ )
  func( *begin );
}

这个 for 循环遍历了容器中的每一个元素,对每个元素调用了仿函数 func,这样就实现了 对“每个元素做同样的事”这样一种编程的思想。

特别的,如果仿函数是一个对象,这个对象是可以有成员变量的,这就让 仿函数有了“状态”,从而实现了更高的灵活性。

 

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. ubuntu14.04 server 安装vmware worktation 12

    0) Do the basic system installation of Ubuntu 14.04 LTS (Server or Desktop) 1) wget the installer wg ...

  2. svn迁移到git仓库并保留commit历史记录

    svn迁移到git仓库并保留commit历史记录 最近在做svn迁移到gitlab,由于之前一直是由svn做版本控制.最简单的方式是将svn的内容export出来,然后添加到gitlab即可.但是,如 ...

  3. ACM/ICPC 之 简单DP-记忆化搜索与递推(POJ1088-滑雪)

    递推型DP 将每个滑雪点都看作起点,从最低点开始逐个由四周递推出到达此点的最长路径的长度,由该点记下. 理论上,也可以将每一点都看作终点,由最高点开始计数,有兴趣可以试试. //经典DP-由高向低海拔 ...

  4. markdown 基本语法

    代码块: ```console.log(1);```--- 标题: # h1## h2### h3 --- 粗斜体: *斜体***粗体*****粗斜体*** --- 强调:`强调` --- 链接:[百 ...

  5. pom.xml中引入局域网仓库

    <repositories> <repository> <id>nexus</id> <name>my-nexus-repository&l ...

  6. 收集android上开源的酷炫的交互动画和视觉效果:Interactive-animation

    查看网址:http://www.open-open.com/lib/view/open1411443332703.html

  7. 【leetcode】Number of Islands(middle)

    Given a 2d grid map of '1's (land) and '0's (water), count the number of islands. An island is surro ...

  8. UVA 11827 Maximum GCD

    F - Maximum GCD Time Limit:1000MS     Memory Limit:0KB     64bit IO Format:%lld & %llu Given the ...

  9. Sql Server 保留几位小数的两种做法

    数据库里的 float momey 类型,都会精确到多位小数.但有时候 我们不需要那么精确,例如,只精确到两位有效数字. 1. 使用 Round() 函数,如 Round(@num,2)  参数 2 ...

  10. Linux环境下段错误的产生原因及调试方法小结

    转载自http://www.cnblogs.com/panfeng412/archive/2011/11/06/2237857.html 最近在Linux环境下做C语言项目,由于是在一个原有项目基础之 ...