STL算法之 copy、copy_backward、copy_n
2016-07-20 23:03
561 查看
这些函数都定义在stl_algobase.h里面,使用时只需要包含 algorithm 头文件就行。
该函数完成的功能:把[first, last)区间的元素拷贝到以result为起始地址的空间中。
以上是copy的泛化版本,除此之外,还有两个特化版本:
这两个函数是针对char类型的指针,如果拷贝的是char类型的指针所指向的内存,则直接调用特化版本,这两个函数的内部其实就是调用memmove()函数,然后返回拷贝目的数据区间的下一个位置的地址。
下面先大概图解一下对于copy的具体调用过程。
![](https://img-blog.csdn.net/20160720224537454)
下面是copy的源码,函数实现定义在stl_algobase.h中: 接下来用源码解释上面这幅函数调用图。
(ps : 在下面的源码中,我用具有指导作用的标题表示他们的调用关系;
例如:copy函数有三个版本,我用1.1表示它的第一个泛化版本,用1.2表示第二个特化版本,用1.3表示它的第三个特化版本,用1.1.1表示__copy_dispatch的第一个泛化版本,依次类推。)
1.1
copy的泛化版本:
在上面的函数调用中,其实是copy调用了一个仿函数,__copy_dispatch是一个结构体,该结构体重载了operator()运算符,
__copy_dispatch 结构定义如下:
1.2
copy的特化版本1:
1.3
copy的特化版本2:
1.1.1
copy内部,__copy_dispatch
这个函数的作用是把 [first, last) 区间的元素拷贝到以result为起始地址的内存中,这里有几种情况需要解释下:
![](https://img-blog.csdn.net/20160720225033536)
copy内部,__copy_dispatch
这个特化版本与上个特化版本的区别在于:第一个是接受一个input_iterator_tag类型的参数,而第二个接受一个random_access_iterator_tag类型的参数。
不同的参数内部调用也不同,如果是随机迭代器,则可以把last与first相减得到输入区间的大小,从而避免使用迭代器的 operator!= 比较两个迭代器是否相等。。。
由此也可见copy为了效率,无所不用其极。。。。。
下面是_copy_d的实现(把它单独封装起来,是因为下面还有一个地方会用到这个函数):
1.1.2
__copy_dispatch的特化版本1:
__copy_dispatch的特化版本2:
上面两个特化版本针对的是原生指针,对于这种情况,STL调用了__copy_t,下面是其定义:
对于__copy_dispatch的2个特化版本,里面用到了 has_trivial_assignment_operator 类型,他本身是一个struct __false_type{}; 或者struct __true_type{}; 类型;
如果是struct __false_type{}; 类型,表示迭代器所指元素的值类型T没有”无关紧要的赋值语句”,就要调用
如果是struct __true_type{}; 类型,表示迭代器所指元素的值类型T有”无关紧要的赋值语句”,就要调用
下面是copy的两个特化版本:
由于是char * 类型的参数,所以直接调用库函数memmove来完成。
![](https://img-blog.csdn.net/20160720225344474)
copy_backward与copy的调用结构基本一致,但也有稍许区别:例如
由于copy_backward与copy的结构基本一致,并且比copy要简单许多,这里只给出源码,就不解释了。
__copy_backward_dispatch的泛化版本:
__copy_backward_dispatch的特化版本1:
__copy_backward_dispatch的特化版本2:
有“无关紧要的赋值操作符” 会执行下面这个函数:
没有“无关紧要的赋值操作符” 会执行下面这个函数:
底层的具体实现之一的函数(另一个是memmove):
这个函数的关系并不复杂,只是内部调用了一个__copy_n函数。而__copy_n函数只是根据接收到的迭代器型别,执行不同的策略。
比较简单,就不画图了; 下面是源码 :
对于输入型迭代器,因为输入迭代器可以接收前向迭代器、双向迭代器、随机存取迭代器,所以不能唯一确定迭代器型别,需要做最坏打算,就是一个一个地拷贝。
对于随机存取迭代器,因为可以对迭代器随机访问,即 对迭代器加上一个整数,得到另一个迭代器,这样确定了输入区间,就能复用copy()函数了。
copy_n的返回值是一个pair,pair的两个元素都是迭代器,
pair::first指向输入区间的后面一个元素;
pair::second指向输出区间的后面一个元素。
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的具体调用过程。
下面是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为起始地址的内存中,这里有几种情况需要解释下:
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的具体调用过程: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指向输出区间的后面一个元素。
相关文章推荐
- 书评:《算法之美( Algorithms to Live By )》
- 动易2006序列号破解算法公布
- C#递归算法之分而治之策略
- Ruby实现的矩阵连乘算法
- C#插入法排序算法实例分析
- 批处理中Copy与Xcopy命令的区别小结
- C#算法之大牛生小牛的问题高效解决方法
- dos 文件复制 copy命令
- C#算法函数:获取一个字符串中的最大长度的数字
- 超大数据量存储常用数据库分表分库算法总结
- C#数据结构与算法揭秘二
- C#冒泡法排序算法实例分析
- 算法练习之从String.indexOf的模拟实现开始
- C#算法之关于大牛生小牛的问题
- C#实现的算24点游戏算法实例分析
- 经典排序算法之冒泡排序(Bubble sort)代码
- c语言实现的带通配符匹配算法
- 浅析STL中的常用算法
- STL区间成员函数及区间算法总结