Chinaunix首页 | 论坛 | 博客
  • 博客访问: 539785
  • 博文数量: 252
  • 博客积分: 6057
  • 博客等级: 准将
  • 技术积分: 1635
  • 用 户 组: 普通用户
  • 注册时间: 2009-12-21 10:17
文章分类

全部博文(252)

文章存档

2013年(1)

2012年(1)

2011年(32)

2010年(212)

2009年(6)

分类: C/C++

2010-04-18 17:38:35



什么是模板

在编程中,对于各种不同 的数据类型,我们希望能提供一种功能相同的函数,而这些函数的区别仅仅是所处理的类型不同。这一功能 ,我们在C语言中用宏定义来实现,而在C++语言中我们可以用函数重载来实现,但这两种方法各有弊病,于是,我们引入了一种更安全可靠的方法,就是使用函 数模板和类模板。
 
   简单的说,模板是一种工具,使用它程序员可以建立具有通用类型的函数库和类库,这给编写软件带来了方便。
   具体的说,模板是对类型的参数化,将一般程序处理的类型参数化,就可以使用这段程序处理某个类型范围内的若干种类型的对象。这就是参数化的多态性。即, 多个不同类型的对象的操作可以对应同一结构的实现。

模板的种类:  有函数模板和类模板
 
函数模板 :是对一组函数的描述,编译系统对所定义的函数模板不产生函数的代码。只是当编译系统在程序中发现有与函数模板向匹配的函数调用时,就生成一个模板函数, 该函数的函数体和函数模板相同。
 函数模板实际上是一个通用的函数,它可以适应某个范围的不同类型对象的操作,这样做可以避免程序员的重复劳动。也可以增加程序的灵活性和安全性。某些情 况下,函数模板可以代替函数重载。

  定义格式:
template  <(参数化的类型名表)>
<类型> <函数名>(<参数表>)
{
    函数体
}

template  是定义模板的关键字
参数化类型名表是模板参数表,如有多个表项用' ,'号分隔。 格式如下:
  class <标识符1>  ,class <标识符2>,。。。。
标识符是函数中参数化的类型,这里用class 来定义,其含义是说明其后的标识符是参数化的类型名。

模板函数:是某个函数模板的一个实在函数。

  函数模板定义的是模板函数的一种工具,模板函数是函数模板的一种实例化,即
一个函数模板可以生成多个重载的模板函数。 通过如下例子来说明:

 
 #include
template< class T > //函数模板的定义,有一个模板参数t,该函数实现求同一 类型的两数之和。
T Add(T a, T b)
{
   return  a+b;
}
void main()
{

int x1=1,y1=2;          
double x2=2.2,y2=2.8;
char x3='m',y3='n';       //分别用int double char 来替代T 即实现了模板函数
cout<<"add(x1,y1)="< cout<<"add(x2,y2)="< cout<<"add(x3,y3)="<
}


模板规范(Template Specifications)



Template   Specifications  
      模板规范是一系列参数化的类或函数的定义集。  
  语法:  
  模板声明   :    
  template   <模板参数列表   >   declaration    
  模板参数列表:    
  模板参数  
  模板参数列表,   模板参数  
  模板参数:    
  类型参数  
  参数声明    
  类型参数:    
  class   标识符  
  typename标识符  
  模板声明是一系列参数化的类或函数的定义集。  
  The   template   declaration   specifies   a   set   of   parameterized   classes   or   functions.  
  模板参数列表是用逗号分隔的在模板体中使用的类型(以class   identifier,   typename   identifier,或者   template   type的形式出现)或者非类型(non-type)。在声明域中必须是一个函数或类的声明。你可以象实例化一个普通类那样去实例化一个类模板,但是你必 须在尖括号中包括模板参数。调用一个功能化的模板不需要特需的语法。  
  在功能化的模板中,每个模板参数必须在函数的模板参数列表声明中至少出现一次。  
  模板参数列是一个参数列表,它被用在规定代码中的哪一部分将改变的模板函数之中。例如:  
  template<   class   T,   int   i   >   class   MyStack...  
  在这种情况下模板能够使用一个类型(class   T)和一个常数参数(int   i)。模板将再构造函数中使用类型T和常数整型变量i。在MyStack类声明的主体中,你必须参考T标示符。  
  例如:  
  Examples  
  //   Example   of   the   template   keyword  
  template     class   TestClass   {  
  public:  
        char   buffer[i];  
        T   testFunc(T*   p1   );  
  };  
   
  template    
  T   TestClass::testFunc(T*   p1)   {  
          return   *(p1++)  
  };  
   
  //   To   create   an   instance   of   TestClass  
  TestClass   ClassInst;  
  下面的例子显示了怎样向一个模板传递一个参数:  
  template  
  class   X   {  
  };  
   
  template   class   T1,   typename   T2>  
  class   Y   {  
  };  
   
  Y   aY  
  typename关键字可以在模板参数列表中使用,下面的模板声明是一样的。  
  template<   class   T1,   class   T2   >   class   X...  
  template<   typename   T1,   typename   T2   >   class   X...  
  下面的模板参数形式是可行的:  
  template   class   allocator   {};  
  template       typename   Allocator   =   allocator   >   class   stack   {  
  };  
  stack   MyStack;  
  Visual   C++支持在模板参数列表中模板参数的复用机制。例如,下面的代码是合法的:  
  class   Y   {...};  
  template   class   X1   {...};  
  template   class   X2   {...};  
   
  Y   aY;  
   
  X1   x1;  
  X2   x2;  
  模板声明自身不会产生代码,它规定了类或函数系列,以及当被其他的代码引用时将要产生的函数或类。  
  模板声明具有全局或命名区域。  
  Visual   C++执行模板定义的语法检查。Visual   C++5.0   和后续的版本能够发现以前的版本不能发现的错误。编译程序现在能够检测到模板定义,但从不实例化的语法错误。  
  这儿有一个普通的错误列表,它们能够被Visual   C++   4.0编译器编译,但是不能够被Visual   C++   5.0或以后的版本编译。  
  · 一个用户自定义类型在模板声明声明前就被使用,但是它的声明在模板的第一个实例化或使用之前。  
  · template   class   X   {  
  ·     //...  
  ·     Data   m_data;     //Error   Visual   C++   5.0   or   later,   Data   not   defined  
  · };  
  ·  
  · class   Data   {...};  
  ·  
  · void   g()   {   X   x1;   }  
  ·  
  · 解决该问题,只需将类模板X移到数据声明之前。  
  · 成员函数是在类模板外声明的,而且它从不在类模板内声明。例如:  
  · template   class   X   {  
  ·     //no   mf   declared   here  
  · };  
  ·  
  · //This   definition   did   not   cause   an   error   with   Visual  
  · //C++   4.0,   but   it   will   cause   an   error   with   Visual  
  · //C++   5.0   or   later  
  · //  
  · template   void   X::mf()   {...};  
  · 除了声明是类模板外,类标识将始终被认为是普通类。例如下面的用Visual   C++   5.0或以后的版本而不是用Visual   C++   4.0生成的代码将产生一个错误:  
  · template   class   X   {  
  ·     friend   class   Y;     //Parsed   as   Y   less-than  
  ·                     //T   greater-than;  
  ·     Z   mf(   );         //Parsed   as   Z   less-than   T  
  ·                 //greater-than;  
  · };  
  ·  
  · template   class   Y   {...};  
  · template   class   Z   {...};  
  ·  
  · X   x;  
  ·  
  · 为了解决此问题,将Y和Z的声明放到X之前。  
  ·  
  · template   class   Y   {...};  
  · template   class   Z   {...};  
  ·  
  template   class   X   {...};  


模板的特性是静态多态, 是编译时期的多态, 
比如:



template
void fun(){}

fun(1);
fun(2.3);

编译器就只会给你生成个void fun()和void fun(), 这种检查是在编译时期进行的.

比如用这一特性来搞个compile time check, 也叫static check, 比如morden C++ design上的:

template
struct static_assert;

template <>
struct static_assert{};


就可以实现编译期间的assert;

static_assert<1 > 2>();
static_assert<2 < 3>();


摸板现在不支持实现和原型分开, 所以你只能把他们放在同一个文件中, 比如:

template
void fun();

template
void fun(){...}

或者直接

template
void fun(){...}

C++模板的制定三:部分制定C++类

   类模板的多参语法如下:

       template

       class classname

{

       ……

};

T能是所有类型,上一节我们介绍了制定类模板,本节说一下类模板的部分制定,意思就是说只制定模板参数 的一部分,从而得到我们想要的目的,本节直接来描述其语法,我想信大家都不愿意我在这罗嗦,都想直接了解其语法。下面我们就看一个类模板部分制定的例子。

SpecTemplate类的原型如下:

template

class SpecTemplate

{

public:

       SpecTemplate(T t,P p):_valt(t),_valp(p){}

       ~SpecTemplate(){}

       T typeT(){return _valt;}

       void typeT(T& t){ _valt = t;}

       void display(){cout<<_valt<

       ……

 

private:

       T _valt;

       P _valp;

};

假设我们想制定SpecTemplate类的模板参数P,当Pstring类型时,我们希望其相应的SpecTemplate类和通用的SpecTemplate类具有不同的功 能。这时需部分制定SpecTemplate类,部分制定示例如下:

template

class SpecTemplate

{

public:

       SpecTemplate(T t,int p):_valt(t),_valp(p){}

       ~SpecTemplate(){}

       T typeT(){return _valt;}

       void typeT(T& t){ _valt = t;}

       void display(){cout<<_valp<

       ……

 

private:

       T _valt;

       int _valp;

};

这样就完成了类模板的部分制定,从而实现 你特别的需求。这里只是举例,请大家千万别说这样有不没有意义什么的。我只希望大家能明白其用法即可。

:目前的VC6.0尚不支持这种写法,但这是C++标准写法,如果要试请用BCGCC编译器。

文章写的仓促,有错别字或错误请大家多批 评指出。谢谢大家的捧场,在下感激不尽,欢迎和大家交流 (-----袁凯-----)


编写了一个很简单的类 stack
%2Fu%2F20091106%2F09%2F36be1c18-1f21-4afa-99d5-32008d079a42.html
源代码如下:
C/C++ code

//类定义
template <class T>
class MyStack
{
public:
// class constructor
MyStack();

bool isempty() const;
bool isfull() const;
int countNum() const;
bool push(const T &item);
bool pop(const T &item);
// bool push(const Item &item);
// bool pop(Item &item);

private:
// enum {MAX = 10};
// Item items[MAX];
T items[10];
int top;
int nums;
};
// Class automatically generated by Dev-C++ New Class wizard
--------------------------------------------------------------------------------------------
//类实现
#include "mystack.h" // class's header file

// class constructor
template <class T>
MyStack
<T>::MyStack()
{
// insert your code here
top=0;
nums
=0;
}
template
<class T>
bool MyStack<T>::isempty() const
{
return top==0;

}

template
<class T>
bool MyStack<T>::isfull() const
{
return top==MAX;
}

template
<class T>
int MyStack<T>::countNum() const
{
return nums;
}
template
<class T>
bool MyStack<T>::push(const T &item)
{
if(top<MAX)
{
items[top
++]=item;
nums
--;
return item;
}
else
return false;
}

template
<class T>
bool MyStack<T>::pop(const T &item)
{
if(top>0)
{
item
= items[--top];
nums
++;
return true;
}
else
return false;

}


-------------------------------------------
//main函数
#include <cstdlib>
#include
<iostream>
#include
"mystack.h"

using namespace std;

int main()
{

MyStack
<int> iiu;


system(
"PAUSE");
return EXIT_SUCCESS;
}




编写可移植的C++ 模板代码(参考以下网址)
http://hi.baidu.com/dazhao_dbblog/blog/item/a12e093e39d13ceb55e72373.html



写了一个简单的c/c++模板类,共享大家


[code]
#include ;
#include ;
#include ;
#include ;
#include ;
#include ;

using namespace std;

class doctmplx
{
        public:
                doctmplx();
                doctmplx(const string&);
                ~doctmplx();
                int open(const char*);
                int replace(char *src_str, char *dest_str);
                int write(const string&);
                void close();


        protected:
                string filex;
                char *data;
       
};

doctmplx::doctmplx()
{
        filex = "";
        data = NULL;
}

doctmplx::doctmplx(const string &x)
{
        filex = x;
        data = NULL;
}

doctmplx::~doctmplx()
{

}

int doctmplx::open(const char *filename)
{
        filex = filename;
        FILE *fp;
        char *buff;
        long f_size;
        fp=fopen(filename, "rb");
        if(fp==NULL)
        {
                return -1;
        }

        fseek(fp, 0,  2);
        f_size=ftell(fp);
        rewind(fp);

        buff=(char *)malloc(f_size+2);
        fread(buff,  1,  f_size, fp);
        buff[f_size]='\0';
        fclose(fp);

        data = buff;
        return 0;
}

int doctmplx::replace(char *str1, char *str2)
{
          char *buff1, *p, *p1, *p2, *p3;
        int i, len, alloc_size;
        p1=data;
                p3=data;
        i=0;

        while(1)
        {
                        p2=strstr(p1, str1);
                        if(!p2) break;
                        p1=p2+strlen(str1);
                        i++;
        }
        if(i==0) return -1;

        len=strlen(str2)-strlen(str1);
        if(len>;0)
        alloc_size=strlen(p3)+len*i+1;
        else
        alloc_size=strlen(p3)+1;

        buff1=(char *)malloc(alloc_size);

        for(i=0; i         for(i=0, p1=p3; ;)
        {
                p2=strstr(p1, str1);
                if(!p2) break;
                for(p=p1; p                 for(p=str2; *p; buff1[i++]=*(p++));
                p1=p2+strlen(str1);
        }
        for(p=p1; *p; buff1[i++]=*(p++));

                free(p3);
        data=buff1;
        return 0;
}

int doctmplx::write(const string &filew)
{
        ofstream out(filew.c_str());
        if(!out.is_open())
                return -1;
        out << data;
        return sizeof(data);
}


void doctmplx::close()
{
        if(data != NULL)
                free(data);
        data = NULL;
}

int main(int argc, char *argv[])
{
        doctmplx tmp;
        tmp.open("tech.htm");
        tmp.replace("$_ptitle1_", "这是一个测试");
        tmp.replace("$_ptitle2_", "这是二个测试");
        tmp.replace("$_ptitle3_", "这是三个测试");
        tmp.replace("$_ptitle4_", "这是四个测试");
        tmp.write("tech.html");
        tmp.close();
}
[/code]



数据文件:
[code]
原文件:tech.htm
;
;
;
;
;
;
$_ptitle1_
$_ptitle2_
$_ptitle3_
$_ptitle4_
;
;


生成结果文件:tech.html
;
;
;
;
;
;
这是一个测试
这是二个测试
这是三个测试
这是四个测试
;
;

[/code]
阅读(800) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~

chulia200020012010-04-18 17:43:24

C++/CLR泛型与C++模板的对比 http://www.dzsc.com/data/html/2008-8-22/67630.html