C++逆向分析——模版

发布时间 2023-04-09 10:59:31作者: bonelee

模版

假设有一个冒泡排序的函数:

void Sort(int* arr, int nLength)
{
int i, k;
for (i = 0; i < nLength; i++)
{
for (k = 0; k < nLength-1-i; k++)
{
if(arr[k] > arr[k+1]) {
int temp = arr[k];
arr[k] = arr[k+1];
arr[k+1] = temp;
}
}
}
}

但是这个冒泡排序的函数只能对int类型,如果我们想要使用char类型的时候就要重新写一个函数,这就违背了C++的初衷,重复造轮子了:

images/download/attachments/12714553/image2021-4-17_11-17-32.png

那么如何避免重复造轮子呢?C++中使用模板来解决这个问题。

函数模板的语法是这样的:

template<class 形参名, class 形参名, ...>
返回类型 函数名(参数列表) {
函数体;
}

用模板的方式来修改一下这个冒泡排序函数:

template<class T>
void Sort(T* arr, int nLength)
{
int i, k;
for (i = 0; i < nLength; i++)
{
for (k = 0; k < nLength-1-i; k++)
{
if(arr[k] > arr[k+1])
{
T temp = arr[k];
arr[k] = arr[k+1];
arr[k+1] = temp;
}
}
}
}

在当前这个函数中我们只有一个地方需要替换修改,所以在写模板关键词时候尖括号内的class形参只有一个,而我们只需要将需要替换的地方改成形参的名字即可。

那么模版其原理是什么,编译器做了什么工作呢?我们可以看一下如下代码的反汇编代码:

images/download/attachments/12714553/image2021-4-17_11-34-34.png

使用不同类型的数组传入冒泡排序函数,观察一下执行地址:

images/download/attachments/12714553/image2021-4-17_11-33-30.png

可以看见,两个函数的地址完全不一样,这就说明模板的本质就是编译器会在看见不同的传入类型时创建不同的函数

模板除了可以在函数中使用也可以在结构体(类)中使用模板,其格式如下所示:

template<class 形参名, class 形参名, ...>
class 类名 {
...;
}

如下代码,一个结构体,有两个成员函数,一个是比较返回最大的数,一个则是最小的数:

struct Base {
int a;
int b;
 
char x;
char y;
 
int Max() {
if (a>b) {
return a;
} else {
return b;
}
}
 
char Min() {
if (x<y) {
return x;
} else {
return y;
}
}
};

但这个结构体已经指定了成员变量的数据宽度int、char,而我们想要比较任意类型的话,可以使用模板改造下这段代码:

template<class T, class M>
struct Base {
T a;
T b;
 
M x;
M y;
 
T Max() {
if (a>b) {
return a;
} else {
return b;
}
}
 
M Min() {
if (x<y) {
return x;
} else {
return y;
}
}
};

这个模板想使用的话,我们就需要告诉编译器模板中的T、M分别对应什么,所以如果直接使用Base b;则无法编译。

使用如下格式即可:

Base<int, char> b;

images/download/attachments/12714553/image2021-4-17_15-11-54.png