泛型编程是一种很高级的编程思路,感性来理解的话,泛型编程思想编出来的代码往往需要有两个功能:
所有类型的数据都可以处理
所有数据结构所管理的数据都可以处理
总结来说就是不区分数据类型和数据结构
针对加法函数,我们可以会这样定义
int Add(int left, int right)
{
return left + right;
}
但是我们需要传递的参数如果不是两个整型那就不行了。如果需要传递的参数是double,需要重新进行函数的重载
double Add(double left, double right)
{
return left + right;
}
但是如果传递的参数再发生变化,那么就又需要重新定义了,这样就非常麻烦,我们也不可能把所有的类型都定义出来。
这时候就需要用到函数模板了
函数模板的思路是这样的:我们以往的思路是调用函数时传递参数,事先定义好函数。那么我们可不可以抽象出一种函数,它无关于参数类型,只负责实现我们需要的功能,这样我们不就可以实现任意类型了吗?
函数模板就是这样做的,它定义了一种模板,只告诉编译器需要实现什么功能,而传递的参数类型只有在真正调用时才由编译器自行判断,自行生成函数。
我们举这样一个例子,实现两个参数的交换
template<typename T>
void Swap( T& left, T& right)
{
T temp = left;
left = right;
right = temp;
}
可以看到,我们并没有定义出传递的参数是什么类型,而是用T这样一个标志,通过template这样一个关键字定义。
在编译器编译阶段,编译器根据传入的实参类型来推演生成对应类型的函数以供调用。针对上述函数模板,当我们用int类型使用函数模板时,编译器通过对实参类型的推演,发现T需要被定义为int类型,然后会生成一份专门处理int类型的代码。
对于类,通过类创建一个实体的过程叫做实例化,对于函数模板也一样,通过函数模板创建出一个函数的过程也叫做实例化。
函数模板的实例化分为两种类型:显式实例化和隐式实例化。
显式实例化
Add
就是告诉编译器:我需要将当前这个函数模板实例化出的函数是什么类型。(1, 2)
隐式实例化Add(1, 2)
就是不告诉编译器,让编译器通过传递的实参自行判断。
而针对隐式实例化,如果我们传递的参数类型和函数模板不匹配,那么编译器会报错。
显式实例化不同,如果传递的参数类型和函数模板不匹配,那么编译器会进行类型转换,只有当无法类型转换时,编译器才会报错。
另外需要注意的是:
当函数和函数模板同名时,如果传递的参数类型和函数完全一致,那么就之间调用当前的函数,而不会进行函数模板的实例化。如果传递的参数和当前存在的函数参数类型不一致,那么就会调用函数模板,来实例化出更加符合当前参数类型的函数。
编译原理分为两步:
第一步:在函数模板实例化前对函数模板本身的代码进行简单的语法检测(例如看书写的格式是否是一个函数模板格式,甚至在早先的VS编译器中,都不会检测函数模板内部的语法)
第二步:在实例化后,编译器会根据实例化情况结合模板代码生成处理具体类型的代码
与函数模板类似,类模板也是创建出一种模板来,为了实现在通过这个类模板创建出和类型无关的类,从而提高代码的复用率。
例如:在数据结构中需要实现一个顺序表,顺序表的类定义如下
template<class T>
class vector
{
public:
vector(size_t capacity = 3)
{
_capacity = capacity <= 3 ? 3 : capacity;
_array = new T[_capacity];
_size = 0;
}
~vector()
{
if (_array)
{
delete[] _array;
_array = nullptr;
_capacity = 0;
_size = 0;
}
}
void push_back(const T& data)
{
if(_size == _capacity)
reserve(_capacity*2);
_array[_size] = data;
++_size;
}
void pop_back()
{
if (empty())
return;
--_size;
}
bool empty()const
{
return 0 == _size;
}
size_t size()const
{
return _size;
}
size_t capacity()const
{
return _capacity;
}
// []: 下标运算符
T& operator[](size_t index);
const T& operator[](size_t index)const;
void reserve(size_t newcapacity)
{
//...
}
private:
T* _array;
size_t _size;
size_t _capacity;
};
template<class T>
T& vector<T>::operator[](size_t index)
{
assert(index < _size);
return _array[index];
}
template<class T>
const T& vector<T>::operator[](size_t index)const
{
assert(index < _size);
return _array[index];
}
void TestSeqList()
{
// vector s1(10); // 编译失败,因为SeqList不是具体的类,是生成类的模具
vector<int> s1(10);
s1.push_back(1);
s1.push_back(2);
s1.push_back(3);
s1.push_back(4);
cout << s1[0] << endl;
s1[0] = 100;
vector<string> s2(10);
s2.push_back("hello");
s2.push_back("world");
s2.push_back("bites");
s2.push_back("!!!");
}
// 将Stack的代码用模板来封装 || 将堆的代码用模板来封装
int main()
{
TestSeqList();
return 0;
}