C++中的泛型算法(三)
文章目录
- 再探迭代器
- 插入迭代器
- iostream迭代器
- istream_iterator
- ostream_iterator
- 使用流迭代器处理类类型
- 反向迭代器
- 算法命名规范
- 特定容器算法
再探迭代器
除了为每个容器定义的迭代器之外,标准库在头文件iterator中还定义了额外几种迭代器:
- 插入迭代器:绑定到一个容器上,可以用来向容器插入元素;
- 流迭代器:绑定到输入或输出流上,用来遍历所关联的IO流;
- 反向迭代器:向后移动,除了forward_list之外的标准库容器都有反向迭代器;
- 移动迭代器:用来移动元素;
插入迭代器
插入迭代器绑定一个容器。当我们通过其进行赋值时会在容器指定位置插入一个元素。插入器有三种类型:
- back——inserter:创建一个使用push_back的迭代器;
- front_inserter:创建一个使用push——front的迭代器;
- inserter:创建一个使用insert的迭代器,此函数接受第二个参数,该参数必须是一个指向给定容器的迭代器。元素将被插入到给定迭代器之前。
auto it = inserter(c, iter);
*it = val; //插入后it更新指向插入位置后面的位置
//等价于
it = c.insert(it, val);
++it;std::list<int> lst1 = {1, 2, 3};
std::list<int> lst2 = {10, 20};std::copy(lst1.begin(), lst1.end(), std::back_inserter(lst2));
// lst2 = {10, 20, 1, 2, 3}std::copy(lst2.begin(), lst2.end(), std::inserter(lst1, lst1.begin()));
// lst1 = {10, 20, 1, 2, 3}
iostream迭代器
当创建一个流迭代器时,必须指定迭代器将要读写的对象类型。
istream_iterator
一个istream_iterator使用>>来读取流。因此要读取的类型必须定义了输入运算符。创建时可以绑定要一个流。未绑定时,可以当作尾后值:
istream_iterator<int> int_it(cin);
istream_iterator<int> int_eof;while(int_it != int_eof)
{//push_back旧值vec.push_back(*int_it++);
}//另外一种写法
vector<int> vec(int_it, int_eof);
结合算法使用
cout << accumulate(int_it, int_eof, 0);
ostream_iterator
一个ostream_iterator使用<<来读取流。因此要读取的类型必须定义了输出运算符。除了绑定输出流之外,还可以提供一个C风格的字符串。不允许空的或表示尾后位置的ostream_iterator。
ostream_iterator out_iter(cout, " "); //每个输出后加一个空格
//赋值语句实际上将元素写道cout
for(auto e : vec)*out_iter++ = e; //同out_iter = e;
cout << endl;
结合算法使用
//打印vec中的元素
copy(vec.begin(), vec.end(), out_iter);
cout << endl;
使用流迭代器处理类类型
#include <iostream>
#include <iterator>
#include <vector>
#include <string>
#include <sstream>
#include <algorithm>// 定义类
class Person {
public:std::string name;int age;Person() : name(""), age(0) {}Person(const std::string& n, int a) : name(n), age(a) {}
};// 输入运算符重载(用于 istream_iterator)
std::istream& operator>>(std::istream& is, Person& p) {return is >> p.name >> p.age;
}// 输出运算符重载(用于 ostream_iterator)
std::ostream& operator<<(std::ostream& os, const Person& p) {return os << p.name << " (" << p.age << ")";
}int main() {// 用 stringstream 模拟标准输入std::istringstream input("Alice 30\nBob 25\nCharlie 40\n");// 从输入流读取 Person 对象std::istream_iterator<Person> it(input);std::istream_iterator<Person> end;std::vector<Person> people(it, end);// 写入输出流std::ostream_iterator<Person> out(std::cout, "\n");std::copy(people.begin(), people.end(), out);cout << endl;return 0;
}
反向迭代器
反向迭代器就是在容器中从尾元素反向移动的迭代器。其递增递减操作的含义会颠倒过来,++it会移动到前一个元素,–iter会移动到后一个元素。
可以通过rbegin()、rend()、crbegin()、crend()等获取反向迭代器。这些成员函数指向容器尾元素和首前元素:
值得注意的是,反向迭代器需要支持递增和递减运算符,因此forward_list和流迭代器不能创建反向迭代器。
当我们通过反向迭代器找到元素位置,又想用普通迭代器完成后续操作,可以使用reverse_iterator的base成员:
[line.crbegin(), rcomma)
和(rcomma.base(), line.cend()]
指向相同的元素的元素位置。其中rcomma
和rcomma.base
并不是指向一个位置,而是指向相邻的位置。
算法命名规范
- 一些算法使用重载形式传递一个谓词:
unique(beg, end); //使用==比较元素
unique(beg, end, cmp); //使用cmp比较元素
- _if版本:接受一个元素值得算法通常有一个不同名得版本
find(beg, end, val);
find_if(beg, end, pred) //查找第一个令pred为真得元素
- _copy:区分拷贝版本和不拷贝版本
reverse(beg, end); //原容器逆序
reverse(beg, end, dest); //将元素逆序到dest
- 同时支持_copy和_if
remove_if(v1.begin(), v1.end(), [](int i){ return i % 2;}); //删除奇数元素remove_copy_if(v1.begin(), v1.end(), back_insert(v2.begin()), [](int i){ return i % 2;}); //偶数复制到v2,v1不变
特定容器算法
与其他容器不同,list和forward_list因为是链表实现的,应该优先使用成员函数版本得算法而不是通用算法。
链表特有算法,无通用版本: