您的位置:首页 > 编程语言

模板与泛型编程的基础

2016-09-06 18:04 239 查看

1、前言

模板可以让我们以独立任何具体类型的方式编写代码,我们只需要在运行的时候给定具体的类型就可以。而泛型编程可以让我们用同样的方式操作不同的数据对象,比如vector迭代器这些就是很好的例子。模板是泛型编程的基础,因此,了解这模板和泛型编程有助于我们写出适用范围更广的代码。

2、模板的定义

模板的定义分为两种,一种是函数的模板定义,一种是类的模板定义。下面分别讲解。

2.1 函数的模板定义

为了避免出现不同参数类型使用相同函数体而导致多重定义的情况,写一个函数模板是很好的解决方案。比如有如下两个函数:

int conpare(const int &a,const int &b)
{
if(a<b) return 0;
if(b<a) return 1;
return -1;
}

int compare(const string &a,const string &b)
{
if(a<b) return 0;
if(b<a) return 1;
return -1;
}


我们可以发现,除了形参类型不同之外,函数体是一模一样的。为了不重复这样写,可以定义一个模板函数:

template<typename T>
int compare(const T &a,const T &b)
{
if(a<b) return 0;
if(b<a) return 1;
return -1;
}


用关键字template声明一个模板类型,后面接着的是模板形参表,如果有多个模板形参,用逗号分隔。使用模板函数和正常函数一样。

int a=1,b=2;
string c="aa",d="dd";
compare(&a,&b);
compare(&c,&d);


如上,可以发现使用模板函数和正常函数是没有区别的。

如果需要将模板函数定义为内联函数,那么inline修饰符必须在模板形参表之后,函数名之前。比如:

template<typename T> inline int compare(T &a,T &b)
{
//......
}

2.2 类模板的定义

除了定义函数模板外,也可以定义类模板。如下:

template <typename T> class Stack
{
public :
Stack();
~Stack();
Stack(const T &stack){};
T top();
private :
//......
}


同样的也会iyogtemplate开头,后接一个模板形参表,多个形参用逗号分隔,形参在整个类中都可以使用。使用类模板可以如下:

Stack<int> s1;
Stack<double> s2;
Stack<string> s3;

int top1=s1.top();
double top2=s2.top();
string top3=s3.top();


可以发现除了定义对象之外,其它的调用和正常的类是没有区别的。

2.3 模板形参

现在讲解模板形参的声明以及作用域。假设有如下模板函数:

typedef int T;

template<class T> T compare(const T &a,const T &b)
{
T c;
if(a<b)
c=b;
if(b<a)
c=a;
}


在这里我们声明了一个int的别名T以及声明了一个模板函数,模板形参为T。和局部变量一样,模板形成名称会覆盖全局名称,因此c是一个不确定的类型,并不一定是int型。所以如果模板形参含有多个形参值,值的名字必须不相同。如果希望声明一个模板函数,可以如下:

template<class c> c compare(const c &a,const c&b);

template<class d> d compare(const d &a,const d&b);


上面两种都是声明compare的正确形式,也就是说,声明和定义的模板形参名字不必相同。



2.4 typename和class的区别

本质上来说,typename和class是一样的,都是声明后面的标识为类型名。但是typename是作为标准C++加入c++中的,因此在旧的程序是不能使用typename的。在一个模板中,typename和class是可以一起使用的。比如:

template<class T,typename V>
int set(const T ¶m1,const V ¶m2)
{
}


2.5 非类型形参

模板形参分为类型形参和非类型形参。非类型形参一般指的是值。比如:

template <typename T,size_t N>
void set(T (¶m)
)
{
}


其中N是非类型形参,它的值是可以在运行的时候再确定的。比如:

int a[100];
double b[100];

set(a);
set(b);


这样就可以引用模板数组了。



3、模板函数实例化

模板函数本身的类型是不确定的,编译器在根据特定类型产生模板的过程叫做实例化。实例化后的模板是一个确定的类型,类模板在引用实际模板类类型时实例化,函数模板在被调用的时候实例化。

3.1、类模板实例化

前面提到的Queue就是一个类模板,使用它时必须声明类模板的参数类型。比如:

Queue<string> q;


string会替代每次类型形参的出现。而如果我们不指定特定类型去实例化模板的话,是会报错的。比如:

Queue q


因此类模板形参是必须的。

3.2、函数模板实例化

函数模板实例化过程,是可以根据实参的类型进行自动转换的,但是两个实参的类型必须相同或者可以进行显示转换,因为函数模板实例化过程中会涉及到一个匹配的问题,只有匹配成功了,才可以进行调用。比如:

int a;
string b;
compare(a,b);


上述的compare函数模板会实例化失败。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: