分享
 
 
 

C++中的模板(templates)

王朝c/c++·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

作者:苗新东

什么是模板

模板是根据参数类型生成函数和类的机制(有时称为“参数决定类型”)。通过使用模板,可以只设计一个类来处理多种类型的数据,而不必为每一种类型分别创建类。

例如,创建一个类型安全函数来返回两个参数中较小的一个,如果不使用Templates,必须要编写一系列如下的函数:

// min for ints

int min( int a, int b )

return ( a < b ) ? a : b;

// min for longs

long min( long a, long b )

return ( a < b ) ? a : b;

// min for chars

char min( char a, char b )

return ( a < b ) ? a : b;

//etc...

使用templates,可以减少重复部分,形成一个函数:

template <class T> T min( T a, T b )

return ( a < b ) ? a : b;

模板能够减少源代码量并提高代码的机动性而不会降低类型安全。

何时使用模板

模板经常被用来实现如下功能:

Ø 创建一个类型安全的集合类(例如,堆栈)用来处理各种类型的数据

Ø 为函数添加额外的类型检查以避免获得空指针

Ø 合并操作符重载组来修改类型行为(例如智能指针smart pointer)

大多数以上应用可以不用模板实现;但是,模板具有以下几个优势:

Ø 开发容易。你可以只为你的类或函数创建一个普通的版本代替手工创建特殊情况处理。

Ø 理解容易。模板为抽象类型信息提供了一个直截了当的方法。

Ø 类型安全。模板使用的类型在编译时是明确的,编译器可以在发生错误之前进行类型检查。

函数模板(function templates)

使用函数模板,你可以指定一组基于相同代码但是处理不同类型或类的函数,例如:

template <class T> void MySwap( T& a, T& b )

{

T c( a );

a = b; b = c;

}

这段代码定义了一个函数家族来交换函数的参数值。从这个template你可以产生一系列函数,不仅可以交换整型、长整型,而且可以交换用户定义类型,如果类的构造函数和赋值操作符被适当地定义,MySwap函数甚至可以交换类。

另外,函数模板可以阻止你交换不同类型的对象,因为编译器在编译时知道参数a和b的类型。

你可以像调用一个普通函数一样调用一个函数模板函数;不需要特殊的语法。例如:

int i, j;

char k;

MySwap( i, j ); //OK

MySwap( i, k ); //Error, different types.

可以对函数模板的template参数作外部说明,例如:

template<class T> void f(T) {...}

void g(char j) {

f<int>(j); //generate the specialization f(int)

}

当template参数在外部说明时,普通固定的类型转换会转换函数的参数为相应的函数模板参数。在上面的的例子中,编译器会将(char j)转换成整型

类模板(class templates)

可以使用类模板创建对一个类型进行操作的类家族。

template <class T, int i> class TempClass

{

public:

TempClass( void );

~TempClass( void );

int MemberSet( T a, int b );

private:

T Tarray[i];

int arraysize;

};

在这个例子中,模板类使用了两个参数,一个类型T和一个整数i,T参数可以传递一个类型,包括结构和类,i参数必须传第一个整数,因为I在编译时是一个常数,你可以使用一个标准数组声明来定义一个长度为i的成员数组

模板与宏的比较(Templates vs. Macros)

在很多方面,模板类似预处理宏,用给定的类型代替模板的变量。然而,模板和宏有很大的区别:

宏:

#define min(i, j) (((i) < (j)) ? (i) : (j))

模板:

template<class T> T min (T i, T j) { return ((i < j) ? i : j) }

使用宏会带来如下问题:

Ø 编译器没有办法检查宏的参数的类型是否一致。宏的定义中缺少特定类型的检查。

Ø 参数i和j被被调用了2次。例如,如果任一个参数有增量,增量会被加两次。

Ø 因为宏被预处理程序编译,编译器错误信息会指向编译处的宏,而不是宏定义本身。而且,在编译阶段宏会在编译表中显露出来。

模板和空指针的比较(Templates VS. Void Pointers)

现在很多用空指针实现的函数可以用模板来实现。空指针经常被用来允许函数处理未知类型的数据。当使用空指针时,编译器不能区分类型,所以不能处理类型检查或类型行为如使用该类型的操作符、操作符重载或构造和析构。

使用模板,你可以创建处理特定类型的数据的函数和类。类型在模板定义里看起来是抽象的。但是,在编译时间编译器为每一个指定的类型创建了这个函数的一个单独版本。这使得编译器可以使用类和函数如同他们使用的是指定的类型。模板也可以使代码更简洁,因为你不必为符合类型如结构类型创建特殊的程序。

模板和集合类(Templates and Collection Classes)

模板是实现集合类的一个好方法。第四版及更高版本的Microsoft Foundation Class Library使用模板实现了六个集合类:CArray, CMap, CList, CTypedPtrArray, CtypedPtrList和 CtypedPtrMap。

MyStack集合类是一个简单的堆栈的实现。这里有两个模板参数,T和i,指定堆栈中的元素类型和堆栈中项数的最大值。push 和 pop成员函数添加和删除堆栈中的项,并在堆栈底部增加。

template <class T, int i> class MyStack

{

T StackBuffer[i];

int cItems;

public:

void MyStack( void ) : cItems( i ) {};

void push( const T item );

T pop( void );

};

template <class T, int i> void MyStack< T, i >::push( const T item )

{

if( cItems > 0 )

StackBuffer[--cItems] = item;

else

throw "Stack overflow error.";

return;

}

template <class T, int i> T MyStack< T, i >::pop( void )

{

if( cItems < i )

return StackBuffer[cItems++]

else

throw "Stack underflow error.";

}

模板和智能指针(Templates and Smart Pointers)

C++允许你创建“智能指针”(“smart pointer”)类囊括指针和重载指针操作符来为指针操作增加新的功能。模板允许你创建普通包装来囊括几乎所有类型的指针。

如下的代码概括了一个简单的计数垃圾收集者参考。模板类Ptr<T>为任何从RefCount继承的类实现了一个垃圾收集指针。

#include <stdio.h>

#define TRACE printf

class RefCount {

int crefs;

public:

RefCount(void) { crefs = 0; }

~RefCount() { TRACE("goodbye(%d)\n", crefs); }

void upcount(void) { ++crefs; TRACE("up to %d\n", crefs);}

void downcount(void)

{

if (--crefs == 0)

{

delete this;

}

else

TRACE("downto %d\n", crefs);

}

};

class Sample : public RefCount {

public:

void doSomething(void) { TRACE("Did something\n");}

};

template <class T> class Ptr {

T* p;

public:

Ptr(T* p_) : p(p_) { p->upcount(); }

~Ptr(void) { p->downcount(); }

operator T*(void) { return p; }

T& operator*(void) { return *p; }

T* operator->(void) { return p; }

Ptr& operator=(Ptr<T> &p_)

{return operator=((T *) p_);}

Ptr& operator=(T* p_) {

p->downcount(); p = p_; p->upcount(); return *this;

}

};

int main() {

Ptr<Sample> p = new Sample; // sample #1

Ptr<Sample> p2 = new Sample; // sample #2

p = p2; // #1 will have 0 crefs, so it is destroyed;

// #2 will have 2 crefs.

p->doSomething();

return 0;

// As p2 and p go out of scope, their destructors call

// downcount. The cref variable of #2 goes to 0, so #2 is

// destroyed

}

类RefCount 和 Ptr<T>共同为任何一个从RefCount继承的能够提供整数的类的每一个实例提供了一个简单的垃圾收集解决方案。注意使用一个参数类如Ptr<T>代替多个一般类如Ptr的主要好处在于这种形式是完全的类型安全的。前面的代码保证Ptr<T>可以被用在几乎任何T* 使用的地方;相反,一个普通类Ptr只能提供到void*固有的转换。

例如,考虑一个用来创建和处理文件垃圾收集的类,符号、字符串等等。根据类模板Ptr<T>,编译器可以创建模板类Ptr<File>,Ptr<Symbol>, Ptr<String>等等,和它们的成员函数:Ptr<File>::~Ptr(), Ptr<File>::operator File*(), Ptr<String>::~Ptr(), Ptr<String>::operator String*()等等。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有