c++ STL复制算法:copy()、copy_backward()、copy_n()、copy_if()详解_算法

复制是应用很广的一个功能,主要函数有:

  • copy():从序列的第一个元素起进行正向复制。
  • copy_backward():从序列的最后一个元素起进行反向复制。
  • copy_n():从源容器复制指定个数的元素到目的容器中。
  • copy_if():从源序列复制使谓词返回 true 的元素,所以可以把它看作一个过滤器。

copy函数模板的行为等效于:

template<class InputIterator, class OutputIterator>
  OutputIterator copy (InputIterator first, InputIterator last, OutputIterator result)
{
  while (first!=last) {
    *result = *first;
    ++result; ++first;
  }
  return result;
}

该函数功能是“正向一正向”复制,把输入迭代器[first,last)间的元素依次复制到输出迭代器x中,并返回输出迭代器的尾指针。


例子:

#include <vector>
#include <iterator>
#include <iostream>

using namespace std;
int main() {
    int a[5] = {1, 2, 3, 4, 5};
    int b[5];
    vector<int> v;
    
    copy(a, a + 5, b);
    copy(a, a + 5, back_inserter(v));
    
    cout<< "原始a数组为: ";
    copy(a, a + 5, ostream_iterator<int>(cout, " "));
    cout<< "\nb数组为: ";
    copy(b, b + 5, ostream_iterator<int>(cout, " "));
    cout<< "\nvector向量为: ";
    copy(v.begin(), v.end(), ostream_iterator<int>(cout, " "));
    return 0;
}

c++ STL复制算法:copy()、copy_backward()、copy_n()、copy_if()详解_复制算法_02

copy函数非常简洁,以前编程往往是用一个循环来完成源数据到目的数据的复制,现在仅是一条语句,只要给出源数据待复制区间的迭代起止指针及输出迭代器起始指针就可以了。

需要注意:

  1. copy函数的范围规定为[first,last),左边是闭区间,右边是开区间。所以a数组全部复制到b数组一定是copy(a,a+5,b),而不能是copy(a,a+4,b)。
  2. 若目的数据类型是数组,则一定要保证它的内存空间大于或等于源待复制数据空间的大小。

例如上文中是b[5],与源复制数据a[5]内存空间大小相等;若目的数据类型是基本序列容器vector、 list等,则由于可通过back_inserter, front_inserter插入迭代器动态改变容器的大小,因此对基本序列容器对象内存空间没有特殊限制。但是若不用back_inserter,例如本题直接写成copy(a,a+5,v),则编译通不过。


copy_backward函数模板的行为等效于:

template<class BidirectionalIterator1, class BidirectionalIterator2>
  BidirectionalIterator2 copy_backward ( BidirectionalIterator1 first,
                                         BidirectionalIterator1 last,
                                         BidirectionalIterator2 result )
{
  while (last!=first) *(--result) = *(--last);
  return result;
}

该函数功能是“反向一反向”复制,把 BidIt1中的(last,first]间元素依次复制到写双向迭代器x中,返回写功能输出迭代器的首指针。

例子:

#include <vector>
#include <iterator>
#include <iostream>

using namespace std;
int main() {
    vector<int> v { 1, 2, 3, 4, 5 };
    v.resize(v.size() + 2);
    cout << "原始a数组为: ";
    copy(v.begin(), v.end(), ostream_iterator<int>(cout, " "));
    copy_backward(v.begin(), v.begin() + 5, v.end());
    cout << "\t" << endl;
    copy(v.begin(), v.end(), ostream_iterator<int>(cout, " "));
    return 0;
}

c++ STL复制算法:copy()、copy_backward()、copy_n()、copy_if()详解_复制算法_03

copy_backward() 算法类似于copy()算法 那样复制元素,但是从最后一个元素开始直到第一个元素。

c++ STL复制算法:copy()、copy_backward()、copy_n()、copy_if()详解_算法_04

copy_backward() 会复制前两个迭代器参数指定的序列。第三个参数是目的序列的结束迭代器,通过将源序列中的最后一个元素复制到目的序列的结束迭代器之前,源序列会被复制到目的序列中。copy_backward() 的 3 个参数都必须是可以自增或自减的双向迭代器,这意味着这个算法只能应用到序列容器的序列上。


copy_n函数模板的行为等效于:

template<class InputIterator, class Size, class OutputIterator>
  OutputIterator copy_n (InputIterator first, Size n, OutputIterator result)
{
  while (n>0) {
    *result = *first;
    ++result; ++first;
    --n;
  }
  return result;
}

copy_n函数: 第一个参数是指向第一个源元素的输入迭代器,第二个参数是需要复制的元素的个数,第三个参数是指向目的容器的第一个位置的迭代器。返回一个输出迭代器,它指向最后一个被复制元素的下一个位置。

因为标识符InputIterator和 OutputIterator都是模板参数,所以它们就像T和U一样。然而,STL文档使用模板参数名称来表示参数模型的概念。因此上述声明告诉我们,区间参数必须是输入迭代器或更高级别的迭代器,而指示结果存储位置的迭代器必须是输出迭代器或更高级别的迭代器。

例子:

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

using namespace std;
int main() {
    vector<int> v { 1, 2, 3, 4, 5 };
    deque<int> q;
    cout << "原始a数组为: ";
    copy(v.begin(), v.end(), ostream_iterator<int>(cout, " "));
    copy_n(v.rbegin() + 1, 3, back_inserter(q));
    cout << "\t" << endl;
    copy(q.begin(), q.end(), ostream_iterator<int>(cout, " "));
    return 0;
}

c++ STL复制算法:copy()、copy_backward()、copy_n()、copy_if()详解_算法_05

当然,copy_n() 的目的地址也可是以流迭代器:

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

using namespace std;
int main() {
    vector<int> v { 1, 2, 3, 4, 5 };
    deque<int> q;
    cout << "原始a数组为: ";
    copy(v.begin(), v.end(), ostream_iterator<int>(cout, " "));
    cout<<endl;
    copy_n(v.rbegin() + 1, v.size() - 1, ostream_iterator<int>(cout, " "));
    return 0;
}

这样会输出 more_names 中除了最后一个元素之外的全部元素。注意,如果被复制元素的个数超过了实际元素的个数,程序会因此崩溃。如果元素的个数为 0 或负数,copy_n() 算法什么也不做。

copy_if函数模板的行为等效于:

template <class InputIterator, class OutputIterator, class UnaryPredicate>
  OutputIterator copy_if (InputIterator first, InputIterator last,
                          OutputIterator result, UnaryPredicate pred)
{
  while (first!=last) {
    if (pred(*first)) {
      *result = *first;
      ++result;
    }
    ++first;
  }
  return result;
}

前两个参数定义源序列的输入迭代器,第三个参数是指向目的序列的第一个位置的输出迭代器,第 4 个参数是一个谓词。返回一个输出迭代器,它指向最后一个被复制元素的下一个位置。

例子:

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

using namespace std;
int main() {
    vector<int> v { 1, 2, 3, 4, 5 };
    deque<int> q;
    cout << "原始a数组为: ";
    copy(v.begin(), v.end(), ostream_iterator<int>(cout, " "));
    copy_if(v.begin(), v.end(), back_inserter(q), [](int i){
        return i % 2 == 0;
    });
    cout << "\t" << endl;
    copy(q.begin(), q.end(), ostream_iterator<int>(cout, " "));
    return 0;
}

c++ STL复制算法:copy()、copy_backward()、copy_n()、copy_if()详解_STL库_06

因为作为第 4 个参数的 lambda 表达式所添加的条件,这里的 copy_if() 操作只会复制 vector中的偶数。

当然,copy_if() 的目的容器也可以是一个流迭代器:

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

using namespace std;
int main() {
    vector<int> v { 1, 2, 3, 4, 5 };
    deque<int> q;
    cout << "原始a数组为: ";
    copy(v.begin(), v.end(), ostream_iterator<int>(cout, " "));
    cout<< endl;
    copy_if(v.begin(), v.end(), ostream_iterator<int>(cout, " "), [](int i){
        return i % 2 == 0;
    });
    return 0;
}

c++ STL复制算法:copy()、copy_backward()、copy_n()、copy_if()详解_算法_07

对算法进行分类的方式之一是按结果放置的位置进行分类。有些算法就地完成工作,有些则创建拷贝。例如,在sort()函数完成时,结果被存放在原始数据的位置上,因此,sort()是就地算法(in-place algorithm);而copy()函数将结果发送到另一个位置,所以它是复制算法(copying algorithm)。transform()函数可以以这两种方式完成工作。与 copy()相似,它使用输出迭代器指示结果的存储位置;与 copy()不同的是,transform()允许输出迭代器指向输入区间,因此它可以用计算结果覆盖原来的值。