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

c++,stl,模板,适配,迭代 · 浏览次数 : 17

小编点评

内容生成时需要带简单的排版,以使内容更易于阅读。以下是一个排版建议: 1. **使用缩进符号:**缩进符号可以使内容更易于阅读。例如,可以使用 `}` 来缩进 `}`。 2. **使用分隔符:**使用分隔符可以使内容更易于阅读。例如,可以使用 `,'` 来分隔 `,'`。 3. **使用标题:**标题可以使内容更易于阅读。例如,可以使用 `#` 来标题 `#`。 4. **使用颜色:**颜色可以帮助使内容更易于阅读。例如,可以使用 `` 来颜色 ``。 5. **使用格式:**使用格式可以使内容更易于阅读。例如,可以使用 `` 来缩进 ``,可以使用 `` 来颜色 ``。

正文

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;
}

本文作者: 王瑞
本文链接: https://www.lyshark.com/post/e6708835.html
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

与10.1 C++ STL 模板适配与迭代器相似的内容:

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

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

6.1 C++ STL 序列映射容器

Map/Multimap 映射容器属于关联容器,它的每个键对应着每个值,容器的数据结构同样采用红黑树进行管理,插入的键不允许重复,但值是可以重复的,如果使用`Multimap`声明映射容器,则同样可以插入相同的键值。Map中的所有元素都会根据元素的键值自动排序,所有的元素都是一个`Pair`同时拥有实值和键值,Pair的第一个元素被视为键值,第二个元素则被视为实值,Map 容器中不允许两个元素有相

2.1 C/C++ 使用数组与指针

C/C++语言是一种通用的编程语言,具有高效、灵活和可移植等特点。C语言主要用于系统编程,如操作系统、编译器、数据库等;C语言是C语言的扩展,增加了面向对象编程的特性,适用于大型软件系统、图形用户界面、嵌入式系统等。C/C++语言具有很高的效率和控制能力,但也需要开发人员自行管理内存等底层资源,对于初学者来说可能会有一定的难度。

.NET周报【10月第1期 2022-10-11】

本周精选 继C#实现await/async无栈协程几年后,davidwrighton实现了.NET绿色线程(有栈协程)的原型 https://github.com/dotnet/runtimelab/pull/2002 .NET Runtimelab中绿色线程的原型实现的PR,在不久的将来,.NET

【Azure 事件中心】Event Hub 无法连接,出现 Did not observe any item or terminal signal within 60000ms in 'flatMapMany' 的错误消息

2022-11-03 10:58:21.474 INFO --- [pool-7-thread-1] c.a.m.e.PartitionBasedLoadBalancer []: Load balancer already running 2022-11-03 10:58:51.014 WARN --- [ parallel-2] c.a.m.e.Partition

C++ 中的 lowbit

lowbit 的定义 首先了解 lowbit 的定义 \(lowbit(n)\) ,为 \(n\) 的二进制原码中最低的一位 \(1\) 以及其后面的 \(0\) 所表示的数 举个简单的例子: 将 \(10\) 使用二进制表示为 \(1010\) 其中最低位的 \(1\) 为第2位(\(_{10}1

上周热点回顾(9.25-10.1)

热点随笔: · 在小公司编程是一种什么样的体验? (公众号_陶朱公Boy)· 一个混乱千万级软件项目 (烂人)· 《优化接口设计的思路》系列:第四篇—接口的权限控制 (sum墨)· C#开源且免费的Windows桌面快速预览神器 - QuickLook (追逐时光者)· .NET开发工作效率提升利器

7.2 C/C++ 实现动态链表

动态链表是一种常用的动态数据结构,可以在运行时动态地申请内存空间来存储数据,相比于静态数组和静态链表,更加灵活和高效。在动态链表中,数据元素被组织成一条链表,每个元素包含了指向下一个元素的指针,这样就可以通过指针将所有元素串联起来。使用动态链表存储数据时,不需要预先申请内存空间,而是在需要的时候才向内存申请。当需要添加新的元素时,可以使用`malloc`函数动态地申请内存空间,然后将新的元素插入到

7.4 C/C++ 实现链表栈

相对于顺序栈,链表栈的内存使用更加灵活,因为链表栈的内存空间是通过动态分配获得的,它不需要在创建时确定其大小,而是根据需要逐个分配节点。当需要压入一个新的元素时,只需要分配一个新的节点,并将其插入到链表的头部;当需要弹出栈顶元素时,只需要删除链表头部的节点,并释放其所占用的内存空间即可。由于链表栈的空间利用率更高,因此在实际应用中,链表栈通常比顺序栈更受欢迎。在实现上,链表栈通过使用`malloc

10月TIOBE榜Java跌出前三!要不我转回C#吧

Java又要完了,又要没了,你没看错,10月编程语言榜单出炉,Java跌出前三,并且即将被C#超越,很多资深人士预测只需两个月,Java就会跌出前五。看到这样的文章,作为一名Java工程师我感到……