泛型编程(容器、迭代器、适配器)

面向对象编程关注的是编程的数据方面,而泛型编程关注的是算法。

迭代器(iterator)

模板使得算法独立于储存的数据类型,而迭代器使算法独立于使用的容器类型。(算法参数变为迭代器类型,而不是各种类对象,函数对象,类型参数?)

迭代器不是某种类型,而是一系列的要求。可能需要设计一个迭代器类来满足需要达到的要求,可能只需要常规指针就能达到要求。(迭代器可以是指针,也可以是对象)

每个容器类(vector、list、deque等)定义了相应的迭代器类型。对于其中的某个类,迭代器可能是指针;而对于另一个类,则可能是对象。

STL文献使用术语概念(concept)来描述这一系列的要求。

概念可以具有类似继承的关系(输入出迭代器、正向迭代器、双向迭代器等),例如双向迭代器继承了正向迭代器的功能。然而,

不能将C++继承机制用于迭代器。例如,可以将正向迭代器实现为一个类,而将双向迭代器实现为一个常规指针。对C++而言,这种双向迭代器是一种内置类型(指针类型),不能从类派生而来。然而,从概念上来看,它确实能够继承。

有些STL文献使用术语改进(refinement)来辨识这种概念上的继承。

概念的具体实现被称为模型(model)

迭代器是广义的指针,而指针满足所有的迭代器要求。

迭代器是STL算法的接口,而指针是迭代器,因此STL算法可以使用指针来对基于指针的非STL容器进行操作。

#include "iostream"
#include "algorithm"

using namespace std;

int main()
{
	int arr[5] = {5,2,3,4,5};
	sort(arr, arr + 5);
	for (int x : arr)
		cout << x << " ";
	cout << endl;

	return 0;
}

由于指针是迭代器,而算法是基于迭代器的,这使得STL算法可以用于常规数组。同样,可以将STL算法用于自己设计的数组形式,只要提供适当的迭代器(可以是指针,也可以是对象)和超尾指示器即可。

适配器(adapter)

适配器模式(Adapter Pattern)是一种补救模式,将一个类的接口转换成客户希望的另外一个接口,从而使原本由于接口不兼容而不能一起工作的类可以一起工作。

容器适配器:改变容器接口。

STL提供两个容器迭代器:queue和stack。它们都是修饰deque后成为另一种风貌的容器。

迭代器适配器:STL为某种迭代器提供了模板,该模板是迭代器概念的一个模型(概念的具体实现),它也是一个适配器(一个类或函数)可以将一些其他接口转换为STL使用的接口。

 有时,一个类将具有您所寻求的功能,但不具有访问该功能的正确接口。例如,STL copy()算法需要一对输入迭代器作为其前两个参数,以给出要复制的值范围。 istream对象可以充当此类数据值的源,但它没有复制算法可以使用的任何迭代器。

类似地,copy()算法的第三个参数是一个输出迭代器,它将复制的值定向到正确的目标。该目标可以是输出流,但输出流不直接提供任何输出迭代器。

通过“调整”要发送的消息来生成其他类对象想要接收的消息,适配器类就像“翻译器”一样。

有一个名为istream_iterator的迭代器适配器类,它提供了copy()算法对输入所期望的接口,并将来自此算法的请求转换为适当的istream操作。另外还有一个名为ostream_iterator,它提供了copy()算法对输出所期望的接口,并将来自算法的请求转换为适当的ostream操作。

#include "iostream"
#include "string"
#include "vector"
#include "iterator"
#include "algorithm"

using namespace std;

void Show(int n)
{
	cout << n << " ";
}

int main()
{
	int cast[5] = { 1,2,3,4,5 };
	vector<int> dice(5);
	copy(cast, cast + 5, dice.begin());
	for(int x: dice)
	  cout << x << " ";
	cout << endl;

	vector<int>::iterator ri;
	for (ri = dice.begin(); ri != dice.end(); ++ri)
		cout << *ri << " ";
	cout << endl;

	ostream_iterator<int, char> out_iter(cout, " ");
	copy(dice.begin(), dice.end(), out_iter); //将dice容器的整个区间复制到输出流
	cout << endl;

	vector<int>::reverse_iterator ro;
	for (ro = dice.rbegin(); ro != dice.rend(); ++ro)
		cout << *ro << " ";
	cout << endl;
	for_each(dice.rbegin(), dice.rend(), Show);
	cout << endl;

	return 0;
}
/*out_iter对象构造函数的第一个参数cout指出了要使用的输出流,它也可以是用于文件输出的流。
  后一个字符串参数是在发送给输出流的每个数据项后显示的分隔符*/

copy()的前两个迭代器参数表示要复制的范围,最后一个迭代器参数表示要将第一个元素复制到什么位置。前两个参数必须是(或最好是)输入迭代器,最后一个参数必须是(或最好是)输出迭代器。

这里使用ostream_iterator模板(适配器),将其他接口转换为STL使用的接口。代码中现在out_iter迭代器是一个接口,让你使用cout来显示信息。

/*也可以不创建命名的迭代器,而直接构建一个匿名的迭代器。即可以这样使用适配器*/
copy(dice.begin(),dice.end(),ostream_iterator<int,char>(cout," "));

再举个例子参考

  • Insert Iterator:将容器绑定到back_insert_iterator、front_insert_iterator、insert_iterator。它们都是一个类,对它们的赋值操作将转换为对绑定容器的插入操作。为了操作方便,向用户提供的是一个函数,函数中才创建上述类。

以back_inserter()函数为例:

template <class Container>
inline back_insert_iterator<Container> back_inserter(Container& x) {
  return back_insert_iterator<Container>(x);     // 以容器为参数,创建迭代器适配器
}

 注意,一般迭代器适配器不会以迭代器作为参数,这里通过传入一个容器创建一个迭代器适配器。

下面是back_insert_iterator类的定义:

template <class Container>
class back_insert_iterator {
protected:
  Container* container; // 注意,这里是一个指向容器的指针
public:
  typedef output_iterator_tag iterator_category;     // 输出迭代器,只支持自增
  typedef void                value_type;
  typedef void                difference_type;
  typedef void                pointer;
  typedef void                reference;
 
  explicit back_insert_iterator(Container& x) : container(&x) {}     // 与容器相绑定
  back_insert_iterator<Container>&
  operator=(const typename Container::value_type& value) { 
    container->push_back(value);
    return *this;
  }
  back_insert_iterator<Container>& operator*() { return *this; }
  back_insert_iterator<Container>& operator++() { return *this; }
  back_insert_iterator<Container>& operator++(int) { return *this; }
};

可以看到,迭代器适配器提供了自增和接引用的接口,但是实际的功能被关闭了。上述代码的关键在于,对迭代器适配器的赋值变为了对容器的插入操作。

下面是作者写的一个类似于上面的迭代器适配器:

#include <iostream>
#include <vector>
 
using namespace std;
 
template <class Container>
class my_back_insert_iterator {
protected:
  Container* container;
public:
  explicit my_back_insert_iterator(Container& x) : container(&x) {}
  my_back_insert_iterator<Container>&
  operator=(const typename Container::value_type& value) {
    container->push_back(value);
    return *this;
  }
};
 
int main()
{
    vector<int> vec;
    my_back_insert_iterator< vector<int> > back_insert(vec);
 
    back_insert = 1;
    back_insert = 2;
    back_insert = 3;
    back_insert = 4;
 
    vector<int>::iterator iter = vec.begin();
    for ( ; iter != vec.end(); ++iter)
        cout << *iter << endl;
 
    return 0;
}

容器种类

STL具有容器概念和容器类型。

概念是具有名称(如容器、序列容器、关联容器)的通用类别。(大类)

容器类型是可用于创建具体容器对象的模板(deque、list、queue、stack、vector、map等)。(小类)

猜你喜欢

转载自blog.csdn.net/weixin_40539125/article/details/84594258
今日推荐