以下来自“5月预报”里提到的那篇文章 文中这里要实现一个遍历算法的泛型。
文章说:
使用模板技术来抽象类型,这样可以写出与类型无关的数据结构(数据容器)。
使用一个迭代器来遍历或是操作数据结构内的元素。
C 语言的代码:
/**
* @brief 遍历数组a[size] 查找是否存在target 不存在返回-1
*
* @param a 查找的数组地址
* @param size 查找的数组大小
* @param target 查找匹配的对象
* @param elem_size 查找的每个元素大小
* @param cmpFn 比对数组元素与target是否相同 相同返回1
* @return int 查找到的第一个匹配的元素下标,或-1
*/
int search(void* a, size_t size, void* target,
size_t elem_size, int(*cmpFn)(void*, void*) )
{
for(int i=0; i<size; i++) { // 遍历
if ( cmpFn (a + elem_size * i, target) == 0 )
return i;
}
return -1;
}
C++泛型版的代码:
/**
* @brief 遍历T类型的数组(始末地址 pStart/pEnd)
* 找到target后返回指向它的指针
*
* @tparam T 模板类 代表各种“类型”
* @tparam Iter “迭代器” 是一种类似于指针的存在
* @param pStart 数组起始位置的指针
* @param pEnd 数组结束位的指针
* @param target 查找匹配的对象
* @return Iter 指向数组中target的指针 或NULL
*/
template<typename T, typename Iter>
Iter search(Iter pStart, Iter pEnd, T target)
{
for(Iter p = pStart; p != pEnd; p++) { // p++需要Iter模板自行实现
if ( *p == target ) // *p需要Iter模板自行实现
return p;
}
return NULL;
}
在 C++ 的泛型版本中,我们可以看到:
使用typename T抽象了数据结构中存储数据的类型。
使用typename Iter,这是不同的数据结构需要自己实现的“迭代器”,由 STL迭代器 基础篇 一文,可知Iter用于 为使用了不同底层实现的STL容器提供统一的遍历接口。
然后,我们对数据容器的遍历使用了Iter中的++方法,这是数据容器需要重载的操作符,这样通过操作符重载也就使遍历操作泛型化。 例如使用 vector
函数的入参中,pStart和pEnd表示遍历的起止。
函数体中,Iter来取得这个“指针”的内容,即“去引用”。这也是通过重载 * 操作符来实现的*。
当然,你可能会问,为什么我们不用标准接口Iter.Next()取代++, 用Iter.GetValue()来取代*,而是通过重载操作符? 其实是为了兼容原有 C 语言的编程习惯。
为了这篇打卡折腾了两天 [😓]
不过 一如既往地,Markdown好评~