> 文章列表 > 【C++】模板(定义和使用)

【C++】模板(定义和使用)

【C++】模板(定义和使用)

模板(定义和使用)

  • 泛型编程
  • 函数模板
    • 函数模板的实例化
    • 函数模板的编译原理
  • 类模板

泛型编程

泛型编程是一种很高级的编程思路,感性来理解的话,泛型编程思想编出来的代码往往需要有两个功能:

所有类型的数据都可以处理
所有数据结构所管理的数据都可以处理
总结来说就是不区分数据类型和数据结构

函数模板

针对加法函数,我们可以会这样定义

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<int>(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;
}