Templates and Data Organization
函数模板和类模板
模板可以实现参数多态性,它对程序处理的对象类型进行参数化,使得单个程序能够处理多种不同类型的对象。
函数模板
函数模板的目的是为了避免重复编写仅在数据类型上有所不同的函数,从而大大提高代码的可重用性,增强软件开发效率。
语法形式是在函数定义前添加语句
template<模板参数列表>
模板参数列表的内容:
- 类型参数:class(或 typename)标识符
- 常量参数:类型说明符标识符
- 模板参数:template<参数列表> class 标识符
示例:
1 | //通用绝对值函数 |
函数模板与函数在本质上是不同的
- 函数模板本身不会生成任何对象代码;只有函数模板的实例才会生成对象代码。
- 被多个源文件引用的函数模板需要与函数体一起放在头文件中,而不仅仅是像普通函数那样的声明。
- 函数指针只能指向函数模板的实例,不能指向函数模板。
类模板
使用类模板可以定义类的模式,使得某些数据成员、某些成员函数的参数、返回值或类中的局部变量可以采用任意类型。
类模板的声明方式与函数模板相同,
1 | //在类模板外定义成员函数 |
线性组
线性组顾名思义,其元素的位置对应于它们的相对关系。它们可以分为直接访问、顺序访问和索引访问。直接访问意味着可以直接跳转到需要访问的位置,而不需要遵循顺序,而顺序访问只能根据元素排列顺序从头开始访问。
直接访问组 - 数组类
需要设计一个可变长度的数组,点击查看源代码,并列出一些需要注意的语法要点。
语法规则规定“=”、“[ ]”、“( )”、“->”只能作为成员函数进行重载,而派生类中的“=”运算符函数将始终隐藏基类中的“=”运算符。
如果我们想在程序中像普通数组一样使用 Array 类对象,我们需要重载指针转换运算符。
指针转换运算符的作用
为了说明重载指针转换运算符的必要性,先看以下程序:
1 |
|
这里,read 函数的第一个参数是一个 int 指针,而数组名 a 是一个 int 地址常量,因此类型完全匹配。如果我们想在程序中像普通数组一样使用 Array 类对象,可以将 main 函数修改如下:
1 | int main () |
会发生什么呢?这次调用 read 时,会发现实际参数类型与形式参数类型不同。此时,编译系统会尝试进行自动类型转换:将对象名转换为 int * 类型。由于 a 是一个自定义类型对象,编译系统提供的自动转换函数无法实现这种转换,所以我们需要编写自己的重载指针类型转换函数。 在 C++ 中,如果你想隐式或显式地将自定义类型 T 的对象转换为类型 S,可以将 operator S 定义为 T 的成员函数。这样,在隐式将 T 类型对象转换为 S 类型,或使用 static_cast 显式转换为 S 类型时,将调用该成员函数。转换运算符的重载函数不需要指定返回值类型,因为在这种情况下重载函数的返回类型与运算符名称一致,因此 C++ 标准规定此类函数不能指定返回值类型(也不应写为 void)。 当对象本身是常量时,为了避免通过指针修改数组内容,对象只能转换为常量指针。
在这个 Array 类中重载指针转换运算符的方式如下:
1 | template<class T> |
顺序访问组 - 链表类
1 | // LinkedList.h |
链表的基本操作:
- 生成链表
- 插入节点
- 查找节点
- 删除节点
- 遍历链表
- 清空链表
栈类
栈类模板实现,点击查看源代码。
栈元素实际上可以使用数组或链表来表示。
栈的基本操作:
- 初始化
- 压栈
- 弹栈
- 清空栈
- 访问栈顶元素
- 检查栈状态(满、空)
队列类
设计为使用数组的循环队列,这在添加和删除时相当麻烦。实际上,我认为使用链表就不需要循环实现。
组数组的组织
简单的排序和搜索相当基础,因此这里插入一些图片和链接以便更好地理解。

I wrote a poem about binary search - labuladong









