然后,我们对数据容器的遍历使用了Iter中的++方法,这是数据容器需要重载的操作符,这样通过操作符重载也就泛型掉了遍历,
为了兼容原有 C 语言的编程习惯我们不用标准接口Iter.Next(),不用Iter.GetValue()来取代*。
在函数的入参上使用了pStart和pEnd来表示遍历的起止。
使用Iter来取得这个“指针”的内容。这也是通过重载 取值操作符来达到的泛型。
说明:所谓的Iter,在实际代码中,就是像vector::iterator或map<int, string>::iterator这样的东西。这是由相应的数据容器来实现和提供的(迭代器)。
C++ STL源码中的find函数
template<class InputIterator, class T> InputIterator find (InputIterator first, InputIterator last, const T& val) { while (first!=last) { if (*first==val) return first; ++first; } return last; } C++的范式例子2: Sum 函数C语言版求和函数
long sum(int *a, size_t size) { long result = 0; for(int i=0; i<size; i++) { result += a[i]; } return result; }C++泛型版(有问题):
template<typename T, typename Iter> T sum(Iter pStart, Iter pEnd) { T result = 0; for(Iter p=pStart; p!=pEnd; p++) { result += *p; } return result; }这里默认了 T result = 0;也就是T假设了 Iter 中出来的类型是T。0假设了类型是int;如果类型不一样,就会导致转型的问题
改进版,需要迭代器
Iter在实际调用者那会是一个具体的像vector::iterator的东西
在这个声明中,int已经被传入Iter中了;所以定义result的T应该可以从Iter中来。这样就可以保证类型是一样的,而且不会有被转型的问题。
1.首先,一个迭代器需要和一个"数据容器"(类)在一起,因为里面是对这个容器的具体的代码实现,对这个容器的迭代。
2.它需要重载一些操作符,比如:取值操作*、成员操作->、比较操作==和!=,还有遍历操作++,等等。
3.然后,还要typedef一些类型,比如value_type,告诉我们容器内的数据的实际类型是什么样子。
4.还有一些,如begin()和end()的基本操作。
5.我们还可以看到其中有一个pointer _ptr的内部指针来指向当前的数据(注意,pointer就是 T*)。
有了迭代器,我们让用户自型传入模板T的类型,解决T result = 0出现的问题
最终Sum的范式写法:
这就是整个 STL 的泛型方法,其中包括:
1.泛型的数据容器;
2.泛型数据容器的迭代器;
3.泛型的算法;
如果我们有一个 员工结构体,再想用sum函数来求和怎么办?
struct Employee { string name; string id; int vacation; double salary; };结构体数组增加了很多数据类型,以前sum函数就不知道怎么办了吧,
vector<Employee> staff; //total salary or total vacation days? sum(staff.begin(), staff.end(), 0);这个例子而言,我想计算员工薪水里面最高的,和休假最少的,或者我想计算全部员工的总共休假多少天。那么面对这么多的需求,我们是否可以泛型一些呢?怎样解决这些问题呢?
引入更抽象化的函数编程——reduce函数
reduce函数 需要增加一个参数 op,这个参数可以是一个函数,来完成我们想要的业务操作。
比如下面的业务操作函数:我们来求员工的工资和、最大工资
double sum_salaries = reduce( staff.begin(), staff.end(), 0.0, {return s + e.salary;} ); double max_salary = reduce( staff.begin(), staff.end(), 0.0, {return s > e.salary? s: e.salary; } ); C++STL中的count_if():