C++模板

C++模板

模板首先是一种设计理念,其次才是一种C++语法。只有先理解了模板,才能更好的使用C++中的模板特性。

模板的概念

为了能理解模板,可以先了解一下模具:为了生产一种产品,例如茶具,需要先设计其模具,有了模具后,能够轻易地成批量的生产出成千上万的产品。模具的特点是一次设计,反复使用。模具具有了模板的重用性特点;但却少了可扩展的特点:其产品是一成不变的,如果某人想把茶具的外形少作改动,则此模具就作废了。

为了兼有重用性和可扩展的优点,需要将不变的部分和变化的部分分开,把不变的部分做成模具骨架,把变化的部分作为模具的子部分存在。例如对汽车,可以认为车架、油路系统、电路系统,轮胎构成了一辆汽车的骨架,至于是什么样的车架、油路系统、电路系统在骨架中不体现出来,只是把这些部分的接口数据全定义出来了,只要满足这些定义的子部分部件,都能往骨架中放。可以这么简单的理解:骨架都有一些占位符组成,占位符组成有其定义,符合这些占位符定义的符号都能放入骨架中,以实现不同的功能。

现在把模板的定义上升到理论高度:“定义一个操作中算法的骨架,而将一些步骤延迟到子类中”[1];“各子类的公共行为被提前提取出并集中到一个公共父类中避免代码重复”[1]

需要说明的是,这里所说的子类不是只对模板类的派生,而是指模板类的参数化类别。模板已是设计模式[1]之一;模板通常有两个角色:

抽象类:定义抽象的原语操作(可以简单的定义了一些接口),其具体实现放到子类中;

具体类:实现原语操作以完成算法中与特定子类相关的步骤,即实现抽象类定义的接口。

模板的语法

C++中,分两个层次使用模板:模板函数,模板类。

模板函数

一般在头文件中定义模板函数,如

#ifndef _POS_H_

#define _POS_H_

template<class T>

T* pos(cosnt T& value, T* beginT* end)

{

for(; begin < end; begin ++)

{

    if(*begin == value)

{

    return begin;

}

 

}

}

#endif

要使用模板函数,需要对模板函数进行实例化,如下的例子使模板被实例化两次:

#include “pos.h”

#include <string>

using namespace std;

int main(int argc, char ** argv)

{

int int_array[30];

string string_array[30];

int iptr = pos(-1, &int_array[0], &int_array[30]);

string* sptr = pos(string(“sam”, &string_array[0], &string_array[30]);

    return 0;

}

模板类

模板类的定义与模板函数类似,可以把模板类的成员函数与类定义一起放到头文件中,如:

#ifndef _VECTOR_H_

#define _VECTOR_H_

template <class T>

class Vector

{

};

 

template <class T>

vector<T>::vector():tptf(new T[10], length(10){}

//对于在cpp文件中的实现方式同样需要如此定义

#endif

模板类的使用也类似,如:

#include <Vector.h>

int main(int argc, char** argv)

{

Vector<int> int_vec;

Vector<string> str_vec;

return 0;

}

模板特化

模板特化分两种:完全特化和偏特化;完全特化是模板的一种穷举方式,是指参数的完全固定化,即所以参数都有一个具体的类型,如:

template<bool> struct CompileTimeChecker{};

template<> struct CompileTimeChecker<true>

{

CompileTimeChecker(…){};

};

后者即是模板类CompileTimeChecker类的特化版本,因为它把bool类型的参数固定为true

而偏特化与完全特化是想对的,只有一个参数的模板类,无法偏特化;这是因为偏特化只能固定模板类的部分参数,而非全部(如果是全部,就变成了完全特化了)。如:

template<class T, class U>

class Conversion

{

};

template <class T>

class Conversion<T, T>

{

public:

    enum{exists = 1, sameType = 1};

}

后者实现了对模板类Conversion的偏特化版本,即两个参数TU类型相同的情况。Visual C++ 6不支持对模板类的偏特化。

type traits

Type Traits是范型编程中用于编译是类型判断的一项技术,其主要思想是利用函数特化(特别是完全特化),来为模板类定义一些辅助常量和函数,以便于在编译时进行类型判断。例如,想对指针和非指针类型做区分,以编译进行不同的处理,就可以使用Type Traits技术。

Template <typename T>

Class TypeTraits

{

private:

   template<class U>struct PointerTraits

   {

enum{result = false;};

typedef NullType PointeeType;

   };

   template<class U> struct PointerTraits<U*>

{

   enum{result = true ;};

  typedef U PointeeType;

;

public:

enum{isPointer = PointerTraits::result};

typedef PointerTraits<T>::PointeeType PointeeType;

};

上面的例子,把非指针类型定义其枚举变量resultfalse,这是一种范型结果;而对指针类型,利用特化版本,另外定义其结果为true;为了对外有一致的接口,对非指针类型用了一个空指针占位NullType,并都定义了类型PointeeType。在模板实例化时,会根据不同的参数类型调用不同的版本。

TypeTraits的主要用途主要是用于代码优化,因为如果知道类型后,就能利用特殊方法进行处理。如对char的复制,可以直接使用memcpy,这是C++中最快的复制方式;对有双CPU的机器,可以使用直接比特流复制等。

模板与编译器

编译器怎么样实现模板的呢?

²        编译:在编译如上的例子程序时,编译器从pos.h中读到了模板定义,并根据在main中实例化的情况,编译器会自动生成两个版本的pos函数:pos(const int&, int*, int*)pos(string&, string*, string*)

²        Borland C++VC Sun Workshop HP aCCG++:编译器在main.o或者main.obj中扩展pos函数两次。

²        EDG-based C++或者G++-frepo参数:编译器直到链接时才展开pos函数,但编译时会在本地数据库(EDGmain.ii-实例化信息文件,G++main.pro-仓库文件)中生产记录参数化信息。

模板的副作用

模板是好东西,但也有其副作用,世界就是这么公平:

对于链接时实例化:链接更加耗时;在实例化时会递归很多此来得到所以成员函数的全部版本。

对眼编译时实例化:目标代码变大;很难有选择地实例化模板类成员函数地几个版本,即实现全部版本。

会带来不必要的代码编写,如模板类中实现了操作符<<,并在其中引用此操作符,在实例化模板类时,编译器会要求你的类实现此操作符,即是你的应用程序根本不会使用此操作符,否则编译失败。

结束语

模板技术常用于类库编程和范型编程,因为其主要思想是提炼算法骨干,延迟细节处理;使用好了模板将使类库代码短小精悍;模板读设计思路是一种抽象和复用,能够提高设计的效率。

参考书目

²        [1] STL源码剖析,侯捷。

²        [2] C++ 设计新思维,Andrei Alexandrescu.

阅读更多
想对作者说点什么? 我来说一句

模板和智能指针(c++)

2011年07月24日 500KB 下载

C++上机实验 实验模板

2011年06月21日 42KB 下载

OpenGL Template(OpengL模板

2011年09月26日 6KB 下载

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭