10.1 C++ STL 模板适配与迭代器

STL(Standard Template Library)标准模板库提供了模板适配器和迭代器等重要概念,为开发者提供了高效、灵活和方便的编程工具。模板适配器是指一组模板类或函数,它们提供一种适配机制,使得现有的模板能够适应新的需求。而迭代器则是STL中的令一种重要的概念,它是一个抽象化的数据访问机制,通过迭代器可以遍历STL容器中的元素。适配器与迭代器两者的紧密配合,使得开发者能够高效地处理容器中的元素,提高了代码的复用性和可维护性。

10.1 函数对象适配器

Bind2nd 是一个函数适配器,可以用来将一个双参函数转换成一个单参函数。使用该适配器可以修改函数中的第二个参数,而将第一个参数保持不变。

bind2nd 适配器的具体用法如下:

template <class Operation>
typename binder2nd<Operation>::type
bind2nd(const Operation& op, const typename Operation::second_argument_type& arg);

其中,Operation是一个二元操作函数对象类型(例如,std::less),而arg是该函数中的第二个参数。

bind2nd 会返回一个binder2nd类型的函数对象,它是一个可调用的单参函数对象,可以代替原始的双参函数对象,并将该函数对象的第二个参数固定为arg,从而实现单参数函数的调用。

如下所示,这段代码实现了绑定参数实现对函数对象的适配,使之可以传递参数,其定义了一个名为MyPrint的类,它继承自二元函数对象类binary_function,并重载了operator()操作符。通过模板参数,指定第一个参数类型为int,第二个参数类型也为int,返回值类型为void。在operator()中,对两个int类型的参数valstart进行加法运算,并输出结果到控制台。

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>

using namespace std;

// binary_function (val= > 参数类型1 start=> 参数类型2 void=> 返回值类型)
class MyPrint :public binary_function<int,int,void>
{
public:void operator()(int val, int start) const {
cout << val + start << endl;
}
};

int main(int argc, char* argv[])
{
vector<int> var = {1,2,3,4,5,6,7,8,9,10};

int num;
cin >> num;

// 让MyPrint函数绑定num参数,传参是传递到MyPrint中
for_each(var.begin(), var.end(), bind2nd(MyPrint(),num));
system("pause");
return 0;
}

10.2 函数指针适配器

Ptr_fun 是一个函数适配器,可以将普通函数转换为函数对象(Functor),从而使得可以以函数对象的方式调用该函数。它通常用于STL提供的算法函数(如 sort、find等),这些算法函数要求传入的参数为函数对象,而普通函数并不满足这个要求。

使用ptr_fun的一般步骤为:

  • 在定义函数时,将函数声明为普通函数类型。
  • 在使用ptr_fun适配器时,通过参数列表将想要转换的函数名作为参数传入ptr_fun中。
  • 将得到的适配后的函数对象作为参数传递给调用该函数的算法函数。

下面是一个简单的例子,当函数无法直接绑定参数是,可以将函数指针适配为函数对象,然后再向内部传递参数:

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>

using namespace std;

void MyPrint(int val,int start)
{
cout << val + start << " ";
}

int main(int argc, char* argv[])
{
vector<int> var = {1,2,3,4,5,6,7,8,9,10};

for_each(var.begin(), var.end(),bind2nd(ptr_fun(MyPrint),100) );
system("pause");
return 0;
}

10.3 容器取反适配器

Not1 是一个函数适配器,用于生成一个新函数对象,将原函数对象的逻辑取反。在使用not1这个适配器时,需要注意函数对象必须是一个一元谓词,也就是说,只接受一个参数并返回布尔值的函数对象。适配后的新函数对象接受一个参数,它的返回值取决于原函数对象的返回值,并将其取反。

以下是 not1 适配器的定义:

template <typename Predicate>
std::unary_negate<Predicate> not1(const Predicate& pred);

其中Predicate是一个一元谓词,而返回值是一个封装了谓词的std::unary_negate对象,它是一个可调用的函数对象,并可以在STL的算法函数中使用。

下面是一个使用not1的例子,我们想要找到第一个大于5的数是多少,但由于加上了not1取反,则输出的数据为小于5的数据。

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>

using namespace std;

class MyPrint : public unary_function<int,bool>
{
public:bool operator()(int val) const {
return val > 5; // 找大于5的数
}
};

int main(int argc, char* argv[])
{
vector<int> var = {1,2,3,4,5,6,7,8,9,10};

// 寻找第一个大于5的数,并遍历出来
vector<int>::iterator pos = find_if(var.begin(), var.end(), not1(MyPrint()));
if (pos != var.end())
cout << *pos << endl;

system("pause");
return 0;
}

10.4 文件流对象拷贝文件

Istream_iterator 和 Ostream_iterator 是STL提供的两种迭代器适配器,它们分别用于将输入流和输出流封装成迭代器的形式,以便于使用STL提供的算法函数处理输入和输出流。

  • istream_iterator 可以通过重载 *、++== 等操作符,从输入流中读取数据,并形成一个可遍历的数据集合。
  • ostream_iterator 可以被用于将某个容器的元素写入输出流,它们提供了一个高效的方式,通过大量数据时不需要定义临时的缓冲区,而是直接将元素写入到流里,这使得它成为了输出大量数据时的好选择。

如下一段代码展示了通过绑定istream输入流对象,实现向前迭代动态拷贝文件到指定目录下的功能实现。

#include <iostream>
#include <iterator>
#include <fstream>
#include <string>
#include <algorithm>

using namespace std;

void Copy_Log(string src_path, string dst_path)
{
ifstream read_file;
ofstream write_file;

read_file.open(src_path, ios::in);
read_file >> noskipws;
write_file.open(dst_path, ios::out);

istream_iterator<char> iter_iFile(read_file);
ostream_iterator<char> iter_oFile(write_file);

copy(iter_iFile, istream_iterator<char>(), iter_oFile);
}

int main(int argc, char* argv[])
{
Copy_Log("c:\\lyshark.txt", "c:\\new.txt");
system("pause");
return 0;
}

10.5 向前/后插入迭代器

Front_insert_iterator 和 Back_insert_iterator 是两种STL提供的迭代器适配器,主要用于实现容器的插入操作。

back_inserter适配器可以将元素插入到容器的尾部,而front_inserter则是将元素插入到容器的头部。这两种适配器都是在使用中间层的帮助下实现容器的插入操作,其主要作用是在输出迭代器(通常是一个容器)的末尾自动添加新的元素。

  • back_insert_iterator:将元素追加到容器的末尾,实现方式是通过调用容器的push_back函数实现。
  • front_insert_iterator:将元素插入到容器的头部,实现方式是通过调用容器的push_front函数实现。

下面是具体用例,通过使用插入迭代器我们可以将一组数据插入到容器中的前或后等位置。

#include <iostream>
#include <iterator>
#include <set>
#include <deque>
#include <vector>
#include <algorithm>

using namespace std;

int main(int argc, char* argv[])
{
// 插入迭代器: 将两个数组合并,并插入到集合容器st中
int iArray1[] = { 1, 3, 5, 7, 9 };
int iArray2[] = { 2, 4, 6, 8, 10, 12, 14, 16 };
set<int> st;

merge(iArray1, iArray1 + 5, iArray2, iArray2 + 8, insert_iterator<set<int>>(st,st.begin()));
copy(st.begin(), st.end(), ostream_iterator<int>(cout, " ")); // 输出 st 内部的元素结果
cout << endl;

// 向前插入迭代器
deque<int> deq = {1,2,3};
front_insert_iterator<deque<int>> fii(deq); // 向前插入
*fii++ = 0;
*fii++ = -1;
copy(deq.begin(), deq.end(), ostream_iterator<int>(cout, " ")); // 输出 deq 内部的元素结果
cout << endl;

// 向后插入迭代器
int iArray3[] = { 1, 2, 3, 4 };
int len = sizeof(iArray3) / sizeof(int);
vector<int> ve = {5,6};

// 将数组元素从后插入到ve容器中
copy(iArray3,iArray3+len,back_insert_iterator<vector<int>>(ve));
copy(ve.begin(), ve.end(), ostream_iterator<int>(cout, " ")); // 输出 deq 内部的元素结果

system("pause");
return 0;
}

10.6 容器反向迭代器

Reverse_iterator 是STL提供的一种用于反向迭代器的适配器。它能够处理正向容器,并将其转换为反向容器,这使得可以使用STL通用算法从容器的末尾向前遍历。

一个reverse_iterator对象接受一个普通迭代器参数,并将该迭代器反转。如此一来,通过++运算符将使迭代器指向前一个元素,而*运算符返回的是它所指向的下一个元素。

下面是reverse_iterator的定义:

template<class Iterator>
class reverse_iterator {
public:
typedef std::reverse_iterator<Iterator> reverse_iterator; // C++11

typedef Iterator iterator_type;
typedef typename Iterator::iterator_category iterator_category;
typedef typename Iterator::value_type value_type;
typedef typename Iterator::difference_type difference_type;
typedef typename Iterator::pointer pointer;
typedef typename Iterator::reference reference;

// 构造函数
reverse_iterator();
explicit reverse_iterator(typename iterator_type::pointer p);
explicit reverse_iterator(const iterator_type& x);
template<class U>
reverse_iterator (const reverse_iterator<U>& rev_it); // C++11

// 迭代器操作
reverse_iterator& operator++();
reverse_iterator operator++(int);
reverse_iterator& operator--();
reverse_iterator operator--(int);
reverse_iterator operator+ (difference_type n) const;
reverse_iterator& operator+= (difference_type n);
reverse_iterator operator- (difference_type n) const;
reverse_iterator& operator-= (difference_type n);
reference operator[](difference_type n) const;
pointer operator->() const;
reference operator*() const;
iterator_type base() const;

// 友元函数
template<class U>
friend class reverse_iterator;
template<class U>
bool operator== (const reverse_iterator<U>& rhs) const;
template<class U>
bool operator!= (const reverse_iterator<U>& rhs) const;
};

下面是一个使用reverse_iterator的例子,该迭代器是一个用随机访问迭代器构造出来的迭代器,用于反向迭代容器元素。

#include <iostream>
#include <iterator>
#include <vector>
#include <list>
#include <algorithm>

using namespace std;

int main(int argc, char* argv[])
{
vector<int> var;
back_insert_iterator< vector<int>> bii(var);
*bii++ = 3;
*bii++ = 9;
*bii++ = 10;

// 反向迭代器定义
reverse_iterator<vector<int>::iterator> rfirst(var.end());
reverse_iterator<vector<int>::iterator> rend(var.begin());

// 从尾到头打印
copy(rfirst, rend, ostream_iterator<int>(cout, " "));

system("pause");
return 0;
}