STL算法之 copy、copy_backward、copy_n

这些函数都定义在stl_algobase.h里面,使用时只需要包含 algorithm 头文件就行。

copy :

STL 的SGI版本中的copy函数声明如下:

template <class InputIterator, class OutputIterator>
inline OutputIterator copy(InputIterator first, InputIterator last, OutputIterator result) ;

该函数完成的功能:把[first, last)区间的元素拷贝到以result为起始地址的空间中。
以上是copy的泛化版本,除此之外,还有两个特化版本:

inline char* copy(const char* first, const char* last, char* result) ;
inline wchar_t* copy(const wchar_t* first, const wchar_t* last,  wchar_t* result) ;

这两个函数是针对char类型的指针,如果拷贝的是char类型的指针所指向的内存,则直接调用特化版本,这两个函数的内部其实就是调用memmove()函数,然后返回拷贝目的数据区间的下一个位置的地址。

下面先大概图解一下对于copy的具体调用过程。
《STL算法之 copy、copy_backward、copy_n》
下面是copy的源码,函数实现定义在stl_algobase.h中: 接下来用源码解释上面这幅函数调用图。
(ps : 在下面的源码中,我用具有指导作用的标题表示他们的调用关系;
例如:copy函数有三个版本,我用1.1表示它的第一个泛化版本,用1.2表示第二个特化版本,用1.3表示它的第三个特化版本,用1.1.1表示__copy_dispatch的第一个泛化版本,依次类推。)

1.1
copy的泛化版本:

template <class InputIterator, class OutputIterator>
inline OutputIterator copy(InputIterator first, InputIterator last,
                           OutputIterator result)
{
 return __copy_dispatch<InputIterator,OutputIterator>()(first, last, result);
}

在上面的函数调用中,其实是copy调用了一个仿函数,__copy_dispatch是一个结构体,该结构体重载了operator()运算符,__copy_dispatch<InputIterator,OutputIterator>()是一个__copy_dispatch类型的匿名对象,该对象能接受first, last, result作为函数参数。
__copy_dispatch 结构定义如下:

template <class InputIterator, class OutputIterator>
struct __copy_dispatch
{
  OutputIterator operator()(InputIterator first, InputIterator last,
                            OutputIterator result) {
 return __copy(first, last, result, iterator_category(first));
  }
};

1.2
copy的特化版本1:

inline char* copy(const char* first, const char* last, char* result) {
  memmove(result, first, last - first);
 return result + (last - first);
}

1.3
copy的特化版本2:

inline wchar_t* copy(const wchar_t* first, const wchar_t* last,
                     wchar_t* result) {
  memmove(result, first, sizeof(wchar_t) * (last - first));
 return result + (last - first);
}

1.1.1
copy内部,__copy_dispatch

template <class InputIterator, class OutputIterator>
inline OutputIterator __copy(InputIterator first, InputIterator last,
                             OutputIterator result, input_iterator_tag)
{
  for ( ; first != last; ++result, ++first)
    *result = *first;
 return result;
}

这个函数的作用是把 [first, last) 区间的元素拷贝到以result为起始地址的内存中,这里有几种情况需要解释下:
《STL算法之 copy、copy_backward、copy_n》

1. 如果输出区间的长度大于输入区间,则之后的区间内容不变;
2. 如果输出区间的长度小于输入区间,则会发生不可预测的结果;
3. 这个函数的循环中,使用两个迭代器是否相等来判断循环是否结束,需调用迭代器的operator!=,开销大,速度慢。

copy内部,__copy_dispatch

template <class RandomAccessIterator, class OutputIterator>
inline OutputIterator
__copy(RandomAccessIterator first, RandomAccessIterator last,
       OutputIterator result, random_access_iterator_tag)
{
 return __copy_d(first, last, result, distance_type(first));
}

这个特化版本与上个特化版本的区别在于:第一个是接受一个input_iterator_tag类型的参数,而第二个接受一个random_access_iterator_tag类型的参数。
不同的参数内部调用也不同,如果是随机迭代器,则可以把last与first相减得到输入区间的大小,从而避免使用迭代器的 operator!= 比较两个迭代器是否相等。。。
由此也可见copy为了效率,无所不用其极。。。。。

下面是_copy_d的实现(把它单独封装起来,是因为下面还有一个地方会用到这个函数):

template <class RandomAccessIterator, class OutputIterator, class Distance>
inline OutputIterator
__copy_d(RandomAccessIterator first, RandomAccessIterator last,
         OutputIterator result, Distance*)
{
  for (Distance n = last - first; n > 0; --n, ++result, ++first)
    *result = *first;
 return result;
}

1.1.2
__copy_dispatch的特化版本1:

template <class T>
struct __copy_dispatch<T*, T*>
{
  T* operator()(T* first, T* last, T* result) {
    typedef typename __type_traits<T>::has_trivial_assignment_operator t;
    return __copy_t(first, last, result, t());
  }
};

__copy_dispatch的特化版本2:

template <class T>
struct __copy_dispatch<const T*, T*>
{
  T* operator()(const T* first, const T* last, T* result) {
    typedef typename __type_traits<T>::has_trivial_assignment_operator t;
    return __copy_t(first, last, result, t());
  }
};

上面两个特化版本针对的是原生指针,对于这种情况,STL调用了__copy_t,下面是其定义:

template <class T>
inline T* __copy_t(const T* first, const T* last, T* result, __true_type) {
  memmove(result, first, sizeof(T) * (last - first));
 return result + (last - first);
}
template <class T>
inline T* __copy_t(const T* first, const T* last, T* result, __false_type) {
 return __copy_d(first, last, result, (ptrdiff_t*) 0);
}

对于__copy_dispatch的2个特化版本,里面用到了 has_trivial_assignment_operator 类型,他本身是一个struct __false_type{}; 或者struct __true_type{}; 类型;
如果是struct __false_type{}; 类型,表示迭代器所指元素的值类型T没有”无关紧要的赋值语句”,就要调用__copy_t(const T* first, const T* last, T* result, __false_type);
如果是struct __true_type{}; 类型,表示迭代器所指元素的值类型T有”无关紧要的赋值语句”,就要调用__copy_t(const T* first, const T* last, T* result, __true_type);

下面是copy的两个特化版本:
由于是char * 类型的参数,所以直接调用库函数memmove来完成。

inline char* copy(const char* first, const char* last, char* result) {
  memmove(result, first, last - first);
 return result + (last - first);
}

inline wchar_t* copy(const wchar_t* first, const wchar_t* last,
                     wchar_t* result) {
  memmove(result, first, sizeof(wchar_t) * (last - first));
 return result + (last - first);
}

copy_backward :

下面先大概图解一下对于copy_backward的具体调用过程:
《STL算法之 copy、copy_backward、copy_n》

copy_backward与copy的调用结构基本一致,但也有稍许区别:例如

1. copy有一个泛化版,两个特化版,而copy_backward只有一个泛化版;
2. copy中的__copy有两个版本,分别用input_iterator_tag和random_access_iterator_tag区分,而copy_backward只有一个__copy_backward泛化版本;

由于copy_backward与copy的结构基本一致,并且比copy要简单许多,这里只给出源码,就不解释了。

template <class BidirectionalIterator1, class BidirectionalIterator2>
inline BidirectionalIterator2 copy_backward(BidirectionalIterator1 first,
                                            BidirectionalIterator1 last,
                                            BidirectionalIterator2 result) {
 return __copy_backward_dispatch<BidirectionalIterator1,
                                  BidirectionalIterator2>()(first, last,
                                                            result);
}

__copy_backward_dispatch的泛化版本:

template <class BidirectionalIterator1, class BidirectionalIterator2>
struct __copy_backward_dispatch
{
  BidirectionalIterator2 operator()(BidirectionalIterator1 first,
                                    BidirectionalIterator1 last,
                                    BidirectionalIterator2 result) {
 return __copy_backward(first, last, result);
  }
};

__copy_backward_dispatch的特化版本1:

template <class T>
struct __copy_backward_dispatch<T*, T*>
{
  T* operator()(T* first, T* last, T* result) {
    typedef typename __type_traits<T>::has_trivial_assignment_operator t;
    return __copy_backward_t(first, last, result, t());
  }
};

__copy_backward_dispatch的特化版本2:

template <class T>
struct __copy_backward_dispatch<const T*, T*>
{
  T* operator()(const T* first, const T* last, T* result) {
    typedef typename __type_traits<T>::has_trivial_assignment_operator t;
    return __copy_backward_t(first, last, result, t());
  }
};

“无关紧要的赋值操作符” 会执行下面这个函数:

template <class T>
inline T* __copy_backward_t(const T* first, const T* last, T* result,
                            __true_type) {
  const ptrdiff_t N = last - first;
  memmove(result - N, first, sizeof(T) * N);
 return result - N;
}

没有“无关紧要的赋值操作符” 会执行下面这个函数:

template <class T>
inline T* __copy_backward_t(const T* first, const T* last, T* result,
                            __false_type) {
 return __copy_backward(first, last, result);
}

底层的具体实现之一的函数(另一个是memmove):

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

copy_n :

从first开始拷贝,拷贝count个元素到以result为起始地址的空间。函数定义如下:

template <class InputIterator, class Size, class OutputIterator>
inline pair<InputIterator, OutputIterator>
copy_n(InputIterator first, Size count,
       OutputIterator result) {
 return __copy_n(first, count, result, iterator_category(first));
}

这个函数的关系并不复杂,只是内部调用了一个__copy_n函数。而__copy_n函数只是根据接收到的迭代器型别,执行不同的策略。
比较简单,就不画图了; 下面是源码 :

对于输入型迭代器,因为输入迭代器可以接收前向迭代器、双向迭代器、随机存取迭代器,所以不能唯一确定迭代器型别,需要做最坏打算,就是一个一个地拷贝。

template <class InputIterator, class Size, class OutputIterator>
pair<InputIterator, OutputIterator> __copy_n(InputIterator first, Size count,
                                             OutputIterator result,
                                             input_iterator_tag) {
  for ( ; count > 0; --count, ++first, ++result)
    *result = *first;
 return pair<InputIterator, OutputIterator>(first, result);
}

对于随机存取迭代器,因为可以对迭代器随机访问,即 对迭代器加上一个整数,得到另一个迭代器,这样确定了输入区间,就能复用copy()函数了。

template <class RandomAccessIterator, class Size, class OutputIterator>
inline pair<RandomAccessIterator, OutputIterator>
__copy_n(RandomAccessIterator first, Size count,
         OutputIterator result,
         random_access_iterator_tag) {
  RandomAccessIterator last = first + count;
 return pair<RandomAccessIterator, OutputIterator>(last,
                                                    copy(first, last, result));
}

copy_n的返回值是一个pair,pair的两个元素都是迭代器,
pair::first指向输入区间的后面一个元素;
pair::second指向输出区间的后面一个元素。

    原文作者:Chengzi_comm
    原文地址: https://blog.csdn.net/Chengzi_comm/article/details/51974119
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞