C++中的模板(templates)

 

作者:苗新东

什么是模板

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

       例如,创建一个类型安全函数来返回两个参数中较小的一个,如果不使用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函数甚至可以交换类。

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

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

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和一个整数iT参数可以传递一个类型,包括结构和类,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) }
   

使用宏会带来如下问题:

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

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

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

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

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

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

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

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

       MyStack集合类是一个简单的堆栈的实现。这里有两个模板参数,Ti,指定堆栈中的元素类型和堆栈中项数的最大值。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*()等等。

  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
译序 《C++ Templates全览》译序 模板template和基于模板泛型编程generic programming、泛型范式generic patterns无疑是当今发展最活跃C++程式设计技术。这种欣欣向荣局面并非源于鼓吹和狂热,而是因为模板及其相关技术威力已经为形形色色成功模板程式库所例证。 模板第一个革命性应用是标准模板程式库Standard Template Library,STL。STL将模板技术在泛型容器和演算法领域运用展现得淋漓尽致。但模板威力远远不止于此。它编译期计算compile-time computation能力以及与设计范式design patterns联合运用愈发吸引更多智力投入,并已成为Boost、Loki这样现代C++程式库之基石。 您现在看到这本书填补了C++模板书籍领域由来已久空白。此前,上有《Modern C++ Design》这样专注于模板高级编程技法和泛型范式著作,下有《The C++ Standard Library》这样针对特定模板框架和组件使用指南。然而,假如您对模板机制缺乏深入理解,您就很难自如“上下”,十有八九会被“卡”住。鉴于此,我向每一位渴望透彻理解C++模板技术读者朋友推荐这本书。 本书内容由浅入深共分四个部分。一、二部分重点介绍模板原理和运作机制。这两部分内容无论是在深度方面还是在广度方面均为空前。三、四部分 戮力论述模板设计技术和高级应用。这两部分内容与《Modern C++ Design》相比,立意有别,各有千秋。 即便是本书最基础第一部分,我相信,大多数C++程序员亦能从学到不少“新知”。而第二部分对模板机制深入讲解,则更是成为一名专家级“模板程式员” 之必备知识。本书后半部分,将有助于您加深对一、二部分所学知识理解,同时也是您得以游刃有余地运用模板编程技术有益示范。此外,还将会帮助您更好地理解和使用STL、Boost和Loki这样模板程式库,甚或激发您创造力。 本书最显著风格是将文字说理和代码样例有机结合,二者相辅相成,相得益彰。说理干净利落,样例短小精悍。整书技术饱满,文字平和,堪称C++领域经典之作。 在C++学习方面,“过犹不及”往往成了不求甚解借口。我们在熟稔物件导向编程同时,不要忘了,物件导向绝非C++全部,模板和泛型编程亦占半壁江山。作为严肃C++程式员您,面对一项早经例证成功技术,还要犹豫什么? 感谢侯捷先生,先生言传身教,荣耀受益终生。感谢姜宏先生,在我最繁忙之际,您鼎力相助使得本书初译工作得以如期完成。感谢内人朱艳,您给予理解、支持和无微不至照料永远是我前进动力。
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值