c++STL算法 copy, uninitialized_copy, copy_backward的使用及源码剖析

copy

参考文档

copy - C++ Reference (cplusplus.com)

函数说明

将区间[first, last)内的元素复制到result指向的内存地址开始的内存区域中

函数声明
template <class InputIterator, class OutputIterator>
  OutputIterator copy (InputIterator first, InputIterator last, OutputIterator result);

底层实现

其实现与以下代码等价

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

一个指向复制的元素末尾的迭代器

测试实例
// copy algorithm example
#include <iostream>     // std::cout
#include <algorithm>    // std::copy
#include <vector>       // std::vector

int main () {
  int myints[]={10,20,30,40,50,60,70};
  std::vector<int> myvector (7);

  std::copy ( myints, myints+7, myvector.begin() );

  std::cout << "myvector contains:";
  for (std::vector<int>::iterator it = myvector.begin(); it!=myvector.end(); ++it)
    std::cout << ' ' << *it;

  std::cout << '\n';

  return 0;
}
代码输出
myvector contains: 10 20 30 40 50 60 70

uninitialized_copy

参考文档

uninitialized_copy - C++ Reference (cplusplus.com)

std uninitialized_copy - 简书 (jianshu.com)

函数说明

将范围 [first,last) 中的元素的副本构造到从 result 开始的范围中,并将迭代器返回到目标范围中的最后一个元素。

与copy相比,result指向的内存区域无需预先申请堆空间

函数声明
template <class InputIterator, class ForwardIterator>
  ForwardIterator uninitialized_copy ( InputIterator first, InputIterator last,
                                       ForwardIterator result );
底层实现

其实现与以下代码等价

template<class InputIterator, class ForwardIterator>
  ForwardIterator uninitialized_copy ( InputIterator first, InputIterator last,
                                       ForwardIterator result )
{
  for (; first!=last; ++result, ++first)
    new (static_cast<void*>(&*result))
      typename iterator_traits<ForwardIterator>::value_type(*first);
  return result;
}

uninitialized_copy源码剖析

所有相关代码

uninitialized_copy
template<typename _InputIterator, typename _ForwardIterator>
inline _ForwardIterator
uninitialized_copy(_InputIterator __first, _InputIterator __last,
                   _ForwardIterator __result)
{
  typedef typename iterator_traits<_InputIterator>::value_type
  _ValueType1;
  typedef typename iterator_traits<_ForwardIterator>::value_type
  _ValueType2;
#if __cplusplus < 201103L
  const bool __assignable = true;
#else
  // trivial types can have deleted assignment
  typedef typename iterator_traits<_InputIterator>::reference _RefType1;
  typedef typename iterator_traits<_ForwardIterator>::reference _RefType2;
  const bool __assignable = is_assignable<_RefType2, _RefType1>::value;
#endif

  return std::__uninitialized_copy < __is_trivial(_ValueType1)
         && __is_trivial(_ValueType2)
         && __assignable >::
         __uninit_copy(__first, __last, __result);
}

 typedef typename iterator_traits<_InputIterator>::value_type
  _ValueType1;
  typedef typename iterator_traits<_ForwardIterator>::value_type
  _ValueType2;
  typedef typename iterator_traits<_InputIterator>::reference _RefType1;
  typedef typename iterator_traits<_ForwardIterator>::reference _RefType2;

以上四句利用iterator_traits获取输入迭代器和前向迭代器的数据类型,iterator_traits是一个存在特化的模板类,使用不同类去实例化该类,就会获得不同的迭代器类型

const bool __assignable = is_assignable<_RefType2, _RefType1>::value;

获取迭代器之间是否可赋值的信息

is_assignable<A, B>

获取类型B是否可以赋值给类型A

如果类型B重载了B&operator=(const A&); 则is_assiganable<A, B> = true;

反之则为false

return std::__uninitialized_copy < __is_trivial(_ValueType1)
         && __is_trivial(_ValueType2)
         && __assignable >::
         __uninit_copy(__first, __last, __result);

is_trivial(_ ValueTypr1)

is_trivial函数的功能是判断一个元素是否为简单类型,那么什么是简单类型

  • 简单类型(trivial type)包括标量类型(scalar type),简单类(trivial class)以及任何此类类型的数组

    • 标量元素(scalar type)的相关概念可查看这篇文章 什么是标量类型(Scalar type) (biancheng.net)

    • 简单类(trivial class)

      • 构造函数,拷贝构造函数,移动构造函数,赋值运算符函数,移动赋值运算符函数和析构函数都为隐含的

      • 没有虚成员

      • 没有带大括号或相等初始化式的非静态数据成员

      • 它的基类和非静态成员(如果有)也必须是简单类

__uninitialized_copy是一个有特化版本的仿函数,它会根据不同的布尔值运行不同版本的仿函数

__uninitialized_copy<false>

这是uninitialized_copy中条件判断失败执行的内容,直接在地址上执行std::_ Construct;
调用std::Construct、 addressof、 Destroy

template<bool _TrivialValueTypes>
struct __uninitialized_copy
{
  template<typename _InputIterator, typename _ForwardIterator>
  static _ForwardIterator
  __uninit_copy(_InputIterator __first, _InputIterator __last,
                _ForwardIterator __result)
  {
    _ForwardIterator __cur = __result;
    __try
    {
      for (; __first != __last; ++__first, (void)++__cur)
        std::_Construct(std::__addressof(*__cur), *__first);
      return __cur;
    }
    __catch(...)
    {
      std::_Destroy(__result, __cur);
      __throw_exception_again;
    }
  }
};

Construct(A,B)的功能:A为一个地址值,B为一个类对象或一个类对象的引用, Construct会将在A指向的位置上构造一个与B相同的类对象

__adressof(A)的功能:获取一个对象的地址值,即使其&(地址操作符)已经被重载

__uninitialized_copy<true>

调用std::copy完成具体操作部分

template<>
struct __uninitialized_copy<true>
{
  template<typename _InputIterator, typename _ForwardIterator>
  static _ForwardIterator
  __uninit_copy(_InputIterator __first, _InputIterator __last,
                _ForwardIterator __result)
  { return std::copy(__first, __last, __result); }
};

copy_backward

参考文档

C++ copy_backward

copy_backward - C++ Reference (cplusplus.com)

函数说明

它会像copy()那样复制元素,但是从最后一个元素开始直到第一个元素

copy_backward()会复制前两个迭代器参数指定的序列

函数声明
template <class BidirectionalIterator1, class BidirectionalIterator2>
  BidirectionalIterator2 copy_backward (BidirectionalIterator1 first,
                                        BidirectionalIterator1 last,
                                        BidirectionalIterator2 result);
示意图

在这里插入图片描述

如图所示,源序列from的最后一个元素是如何先被复制到目的序列to的最后一个元素的。从源序列的方向,将每一个元素依次复制到目的序列的前一个元素之前的位置。在进行这个操作之前,但也可以有更多。copy_backward()算法会返回一个指向最后一个被复制元素的迭代器,在目的序列的新位置,它是一个开始迭代器。

参数列表

目的序列的结束迭代器,通过将源序列中的最后一个函数复制到目的序列的结束迭代器之前。源序列会被复制到目的序列中

copy_backward()的3个参数都必须是可以自增自减的双向迭代器,这意味着这个算法只能应用到序列容器的序列上

返回值

一个指向copy的元素中的第一个元素的迭代器

底层实现

其实现与以下代码等价

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

在序列重叠时,可以用copy()将元素复制到重叠的目的序列剩下的位置—也就是目的序列第一个元素之前的位置。如果想尝试用copy()算法将元素复制到同一个序列的右边,这个操作不会成功,因为被复制的元素在复制到同一个序列的右侧,这个操作不会成功,因为被复制的元素在复制之前会被重写。如果想将它们复制到右边,可以使用copy_backward(),只要目的序的结束迭代器在源序列的结束迭代器的右边

下图 说明了在将元素复制到重叠的序列的右边时,这两个算法的不同。

在这里插入图片描述

上图展示了在序列右边的前三个位置运用copy()和copy_backward()算法的结构。在想将元素复制到右边时,copy() 算法显然不能如我们所愿,因为一些元素在复制之前会被重写。在这种情况下,copy_backward() 可以做到我们想做的事。相反在需要将元素复制到 序列的左边时,copy() 可以做到,但 copy_backward() 做不到。

使用实例
std::deque<string> song{"jingle", "bells", "jingle","all","the","way"
};
song.resize(song.size()+2);//给deque多分配两个元素的空间
std::copy_backward(begin(song), begin(song)+6, end(song));
std::copy(begin(song), end(song), ostream iterator<string>(cout, " "));
cout << endl;

这段代码的输出如下:

jingle bells jingle bells jingle all the way
  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值