c++函数模板

在c语言中,我们能够通过预处理宏替换来逃避编译器的类型检查,处理进行一些对类型要求不强的操作的,比如返回同类型的两个数中较大值就可以使用宏定义,从而可以实现我们对不同内置类型的数据大小比较。
当然这我们知道这是不安全的,我们知道宏是在预编译时期进行的简单替换,而不是函数那样通过参数传递调用。
例如:

#include <iostream>    
using namespace std;    
#define min(a,b) (((a)>(b))?(b):(a))    
int main()    
{    
    int arr[10]={2,3,4,5,7,8,9,10,11,13};    
    int size=10;    
    int *p=&arr[0];    
    int count=0;  
    //统计数组的数据个数  
    while(min(p++,&arr[size]) != &arr[size])    
        ++count;                                                                                                                      
    cout<<count<<endl;    
    return 0;    
} 

使用宏替换返回两个数的较小值,这样不管是int类型的数组还是double类型的数组都可以使用,但是上述的运行的结果是不正确的,在宏替换之后,我们使用了三目运算符,p++操作在一次min()计算中被多重复执行了一次,导致程序运行结果不正确。
那么我们知道c++可以进行函数重载,我们可以通过函数重载解决这个问题吗?肯定是可以的,我们将每一个内置类型的min函数显示定义出来:

int min(int a,int b)      
{      
    return a>b?b:a;      
}      
short min(short a,short b)                                                                                                            
{      
    return a>b?b:a;      
}      
char min(char a,char b)          
{      
    return a>b?b:a;                                                                                                            
}                                                                                                                              
double min(double a,double b)                                                                                                  
{                                                                                                                              
    return a>b?b:a;                                                                                                            
}         

这样我们就可以调用不同类型参数的函数,但是我们发现函数只有参数类型不一样,除此之外,函数形参数目及函数中的定义及函数调用均相同,那么我们能否将参数类型提炼出来根据调用需求再实例化具体的函数 ,有的!那就是函数模板,函数模板就是通过函数在调用时,根据调用的参数类型来确定函数在定义时未确定的参数,这个过程叫函数实例化,通过调用给定参数从而实例化出一个确定类型的参数函数。

template<tempname T>//T是未知的类型,具体什么类型在函数调用时确定
//template 和class作用均相同,这里的class不是类定义的class
T Min(T a,T b)
{
	return a>b?b:a;
}

上面是返回最小值的函数模板,有了这个模板,我们就可以直接调用Min函数处理任意类型的数据,也包括自定义类型的数据,只要该自定义类型重载了 ‘>’ 就可以;
函数模版只是一个说明,并不能直接执行,只有在实例化之后它才成为一个普通可执行的函数。那么该模板在调用时是如何进行实例化的呢?比如我们Min(1,2),那么编译系统会生成以下函数

int Min(int a,int b)
{
	return a>b?b:a;
}

注意:

  1. 函数调用过程中首先调用类型匹配度高的函数,优先调用普通函数;比如模板函数和实例化的函数同时被定义,当调用Max(1,2)时,直接调用int Min(int,int)函数,而不再通过模板实例化。
  2. 函数模板在传参过程中不存在隐式类型转换

非类型模板参数

模板类型的非类型参由有一个普通的参数声明构成,非类型模板参数表示该参数代表了一个潜在的值,而该值被用一个常量值,可以出现在定义的余下部分。该参数一般是常整数(包括枚举类型)或者指向外部链接对象的指针。浮点数和类对象不能作为非类型模板参数。

示例:

//代码来自c++Prime 676页
template <int size> 
Buf{ ... }; 
template <int *ptr> 
class BufPtr { ... }; 
 int size_val = 1024; 
 const int c_size_val = 1024; 
 Buf< 1024 > buf0; // 常量 ok 
 Buf< c_size_val > buf1; // const修饰的常变量 ok 
 Buf< sizeof(size_val) > buf2; //sizeof计算的常量表达式sizeof(int) ok:  
 BufPtr< &size_val > bp0; //全局空间域中的变量地址为常量表达值 ok 
 
 // 错误: 不能在编译时刻被计算出来 
 Buf< size_val > buf3;//编译时size_val的值不确定

下面一个例子作为练习,模拟实习stack栈,以数组作为内部结构:

#include <iostream>                                                                                                                   
using namespace std;
//非类型模板参数的练习,栈实现
template<class T,int MAXSIZE>//MAXSIZWE为非模板类型参数,在编译时期已知
class Stack
{
    private:
        int m_count;
        T elems[MAXSIZE];
    public:
        Stack():m_count(0)
        {}
        bool empty()
        {
            return m_count==0;
        }
        bool full()
        {
            return m_count==MAXSIZE;
        }
        void push(const T& val);
        void pop();
        T& top();
        const T& top()const;
};
template<typename T,int MAXSIZE>
void Stack<T, MAXSIZE>::push(const T& val)
{                             
    if(full())               
    {      
        cout<<"Error! Satck full stack"<<endl;                                                                                        
    }       
    elems[m_count]=val;
    m_count++;           
}          
template<class T,int MAXSIZE>
void Stack<T,MAXSIZE>::pop()
{                   
    if(empty())
    {                         
        cout<<"Error! Stack is empty"<<endl;
    }              
    m_count--;
}                                   
template<class T,int MAXSIZE>
T& Stack<T,MAXSIZE>::top()      
{                  
    if(empty())  
    {                       
        cout<<"Error! Stack is empty"<<endl;
        return elems[0];
    }                           
    return elems[m_count-1];      
 }                             
template<class T,int MAXSIZE>
const T& Stack<T,MAXSIZE>::top() const
{                                                                                                                                     
    if(empty())
    {                  
        cout<<"Error! Stack is empty"<<endl;
        return elems[0];
    }                        
    return elems[m_count-1];
}                   
void testStack()
{                             
    Stack<int,10> s;                        
    s.push(3);     
    s.push(4);
    s.push(5);                      
    cout<<"empty? :"<<s.empty()<<endl;
    cout<<"full? :"<<s.full()<<endl;
    cout<<s.top()<<endl;
    for(int i=0;i<3;i++)
    {                       
        s.pop();                            
    }                   
    cout<<"empty? :"<<s.empty()<<endl;
    cout<<"full? :"<<s.full()<<endl;      
    cout<<s.top()<<endl;      
}                            
int main()                            
{                                                                                                                                     
    testStack();
    return 0;          
}                                           

在上面的Stack实例中,我们如果定义了Stack<int,10> s1和Stack<int,20> s2这两个对象,那么这s1和s2是两个不同的类型,虽然两个对象处理的类型相同,但是实例化出来对象内部的数组初始化大小不同,因此是两个不同的对象,两者之间不能进行类型转化。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值