泛型编程(Generic Programming)是一种语言机制,通过它可以实现一个标准的容器库。像类一样,泛型也是一种抽象数据类型,但是泛型不属于面向对象,它是面向对象的补充和发展。泛型编程在C++上的应用主要体现在两方面:函数模板和类模板,接下来我们通过两个例子说明。
泛型,也就是我们说的任何类型,即不依赖于任何具体的类型。通常我们的操作都是依赖于具体的数据类型的,比如:
int add(int a, int b) { return a + b; } 这个add函数依赖于int类型。你只能以int类型调用(即使是float,也会被转换为int型)。得到的结果也只能是int。那如果我们还需要一个float类型的add算法,就需要再写一个重载程序:
float add(float a, float b) { return a + b; }
虽然在C++中可以通过函数重载来解决这个问题,但是反复写相同算法的函数是比较辛苦的,更重要的是函数重载是静态编译,运行时占用过多内存。
而泛型依赖于模板技术,可以支持任意数据类型,比如:
template <class T> T add( T a, T b) { return a + b; }
这个函数就可以用int,float等类型进行实例化,然后进行调用,这就不依赖任何具体的数据类型。例如:
int result = add<int>(1,2); string result = add<string>("hello","world");
比如对栈的描述:
class stack{
push(参数类型); //入栈
pop(参数类型); //出栈
}
如果把上面的伪代码看作算法描述,没问题,因为算法与参数类型无关。但是如果把它写成可编译的源代码,就必须指明类型,否则是无法通过编译的。使用重载来解决这个问题,即对N种不同的参数类型写N个push和pop算法,这样是很麻烦的。
若对上面的描述进行改造如下:
首先指定一种通用类型T,不具体指明是哪一种数据类型:
class stack<参数模板T>
{
push(T); //入栈算法
pop(T); //出栈算法
}
这里的参数模板T相当于一个占位符,当我们实例化stack时,T会被具体的数据类型替换掉。
若定义对象S为stack类型,在实例化S时若我们将T指定int型,则:
class S { push(int); //入栈 pop(int); //出栈 } 这时,我们可以称class stack<参数模板T>是 类的类,通过它可以生成具体参数类型不同的类。综上,可以看出泛型在C++中的应用主要是模板函数和模板类。
函数模板是一个独立于类型的函数,可以产生函数的特定类型版本。
模板定义以关键字template开始,后接尖括号括住的模板形参表。
模板形参可以是表示类型的类型形参(type parameter),也可以是表示常量表达式的非类型形参(nontype parameter)。如下面的程序中的T是类型形参。
/* implement strcmp-like generic compare function */ #include <iostream> #include <string> using namespace std; template <typename T> //类型形参,若多个类型形参,则<typename T1, typename T2> int compare(const T &v1, const T &v2) { if(v1 < v2) return -1; if(v2 < v1) return 1; return 0; } int main() { cout << compare(1,2) << endl; string s1 = "hello", s2 = "world"; cout << compare(s1, s2) << endl; return 0; }使用函数模板时,编译器会将模板实参绑定到模板形参。编译器将确定用什么类型代替每个类型形参,用什么值代替每个非类型形参,然后产生并编译(称为实例化)该版本的函数。
上面的例子中,编译器用int代替T创建第一个版本,用string代替T创建第二个版本。
在定义的类模板中,使用模板形参作为类型或值的占位符,在使用类时再提供具体的类型或值。例如:
template <typename T> class Queue { public: Queue(); T &front(); const T & front() const; void push(const T&); void pop(); bool empty()const; private: //... }; 与调用函数模板不同,实用类模板时,必须为模板形参指明指定数据类型,例如:
Queue<int> qi; // Queue that holds ints Queue<string> qs; // Queue that holds strings