C++模板
在C++中有一种叫做模板的类型,他是一种特殊的数据类型,使用模板就赋予函数和类更加抽象化的表示,使用模板我们对于一些需要做到同样操作但因为数据类型的不同而导致需要写很多个逻辑相同但是数据类型不同的函数和类。这样既浪费空间也浪费时间,所以为了能够更加高效使用我们自己定义的函数和类型就有了模板这样的一个类型。使用模板能够减少一些不必要的重复代码,使得我们编写的代码更加灵活。
函数模板
基本语法
在C++中使用template
这个关键字来声明类或者函数,一般写法是:
template<typename T>
template<class T>
template<typename (what you want to write)>
template<class (what you want to write)>
在尖括号里面可以写typename
或者class
来作为关键字,上述所说的小括号中的内容可以写T也可以写你自己喜欢的名字,只要合乎声明变量的规则即可。只要在写函数的时在声明函数的前面添加这个关键字就说明此时这个函数或类是函数模板或类模板。
下面以Swap函数为例:
template<typename T>
void Swap(T& var1, T& var2) {
T tmp = var1;
var1 = var2;
var2 = tmp;
}
//实际上就是C++里的一个特殊的类型展开的写法如下:
//平时一般习惯换行写(因为会导致声明函数时语句过长
template<typename T> void Swap(T& var1, T& var2) {
T tmp = var1;
var1 = var2;
var2 = tmp;
}
模板使用
模板其实就是一个抽象的数据类型,可以理解为是一个占位符。 如果我想让我的函数能够接受任何类型的变量,那么我就需要声明一个能够代表任何类型的一个数据类型,这个就是template
这个关键字在这里的用处。这里的T
能够代表任何一个类型。至于具体代表什么类型就得看我们在调用函数的时候给他的类型,C++在运行时会去猜测我们传给这个函数是什么数据类型,根据对应的数据类型执行相应的操作。
普通函数与函数模板
当我们对模板函数进行重载的时候,根据传入的参数来判断到底该执行哪个函数。比如现在有两个Swap函数
template<typename T> void Swap(T& var1, T& var2) {
T tmp = var1;
var1 = var2;
var2 = tmp;
}
void Swap(int& var1, int& var2) {
std::cout << var1 << std::endl;
std::cout << var2 << std::endl;
}
当我们调用这两个函数的时候
int a = 10;
int b = 20;
char c = '1';
char d = '2'
//In this case, it will call the normal function
Swap(a, b);
//In this case, it will call the template function
Swap(c, d);
第一次调用的时候传入的是两个int
型变量,所以C++里就自动匹配到了,符合这个类型的函数,也就是形参均为int
型的这个函数。而不会去调用模板函数。也就是优先级可以这样排列:
template function <-- normal function
模板函数的优先级总是最低的,可以理解为只要有满足需求的我就先使用,直到没有办法了才使用模板函数。
- 这里使用贪心思想这样的设计方式。
函数模板本质上就是写好一个模板让系统知道,然后系统就可以根据实际的情况来生成对应的函数,能够使系统生成传入对应数据变量类型的函数。根据传入不同的数据类型来生成对应的模板函数。如在之前的基础上再调用一次这个函数:
Swap(a, a);
因为这个数据类型是之前在传入a,b的时候已经生成过的函数,所以在编译运行的时候并不会再创建一个函数,在汇编文件里,而是重复调用之前已经生成的函数。通过函数模板来生成不同数据类型的函数来进行调用。存在函数模板时,编译器会对文件进行两次编译,一次在声明的时候编译,一次在调用的时候编译。
学习C++的时候可以使用g++分布遍历来学习C++编译过程中的原理,分别使用三个指令
g++ -E <filename>.cpp -o <filename>.i g++ -S <filename>.i -o <filename>.s g++ -c <filename>.s -o <filename>.o
通过这三个指令就可以看到遍历过程中文件发生的变化。
类模板
与函数模板类似,我们可以定义类模板,这样就能使得类能接受不同类型的参数。但与模板函数不同的是类模板不能及你想那个自动类型推导,,类模板必须显式指定类型。
如下,首先我们编写一个类
template<typename T>
class Person {
public:
//Here is to init parma using in this class
//Dont do any logic task
T id;
T age;
std::string name;
Person(T id, T age, std::string name) {
this->age = age;
this->id = id;
this->name = name;
}
void show() {
std::cout << "ID:" << id << std::endl;
std::cout << "Name:" << name << std::endl;
std::cout << "age:" << age << std::endl;
}
};
然后我们在创建类对象的时候需要这样来创建:
Person<int> p(1, 10, "Amy");
下面我们使用一个排序的例子来做应用
#include <iostream>
template<typename T>
void swap(T& a, T& b) {
T temp = a;
a = b;
b = temp;
}
template<typename T>
void printf_array(T* a, std::size_t len) {
for (std::size_t i = 0; i < len; i++ ) {
std::cout << a[i] << " ";
}
std::cout << std::endl;
}
template<typename T1, typename T2>
void create_array(T1& str, T2& num, std::size_t len) {
for (std::size_t i = 0; i < len; i++) {
str[i] = 'Z' - i;
num[i] = 20 - i;
}
}
template<typename T>
void bubble_sort(T* a, std::size_t len) {
for (std::size_t i = 0; i < len - 1; i++ ) {
for (std:: size_t j = 0; j < len - i - 1; j++) {
if (a[j] > a[j + 1]) {
swap(a[j], a[j + 1]);
}
}
}
}
int main(int argc, char const *argv[]) {
int n;
std::cin >> n;
auto str = new char[n];
auto num = new int[n];
create_array(str, num, n);
printf_array(str, n);
printf_array(num, n);
bubble_sort(str, n);
bubble_sort(num, n);
printf_array(str, n);
printf_array(num, n);
return 0;
}