SGI STL内存基本处理工具:uninitialized_copy/uninitialized_fill/uninitialized_fill_n

内存基本处理工具

STL定义5个全局函数,并非空间配置器的内容,但作用于未初始化空间(空间配置器alloc配置的空间)上,对容器实现有帮助。
5个函数分别是:
1)construct()用于构造;
2)destroy()用于析构的;
3)uninitialized_copy(),对应于高层次函数std::copy();
4)uninitialized_fill(), 对应于高层次函数std::fill();
5)uninitialized_fill_n(),对应于高层次函数std::fill_n()。

1)和2)在前面一篇文章 SGI STL源码 空间配置器 中已经有讲解。本文主要讲解3)~5)。

3)到5)是低层次函数,都能让我 将内存配置与对象的构造行为分离开。如果要使用,应包含。不过,SGI把它们实际定义于<stl_unitialized>。

uninitialized_copy

uninitialized_copy() 将源迭代器区间[first, last)的每个对象拷贝到目的区间[result,...)。如果迭代器指向非POD类型,则uninitialized_copy()会调用copy constructor为输入源[first, last)的每个对象都在目的区间上产生一份复制品。

如果你要实现一个容器,uninitialized_copy() 等会为你带来很大帮助,因为容器的全区间构造函数(range constructor)通常以两个步骤完成:

  • 配置内存区块,足以包含范围内所有元素。
  • 使用uninitialized_copy(),在该内存区块上构造元素。

C++标准规格书要求uninitialized_copy() 具有“commit or rollback”语义,意思是,要么“构造出所有必要元素”,要么(当有任何一个copy constructor失败时)“不构造任何东西”。

uninitialized_copy()源码:

/**
* @brief: 将元素从源迭代器区间[first, last)拷贝到到目的迭代器区间[result,...), 内存由配置器配置alloc
* @tparam: InputIter   源区间对应的迭代器类型
* @tparam: ForwardIter 目的区间的迭代器类型
* @param first 源迭代器区间起始位置
* @param last  源迭代器区间结束位置
* @return 目的区间的结束位置对应迭代器
*/
template <class _InputIter, class _ForwardIter>
inline _ForwardIter
  uninitialized_copy(_InputIter __first, _InputIter __last,
                     _ForwardIter __result)
{
  return __uninitialized_copy(__first, __last, __result,
                              __VALUE_TYPE(__result)); // 萃取result的value type特性, 即迭代器所指对象原生类型
}

/**
* @brief: uninitialized_copy()内部调用, 根据迭代器所指原生类型 萃取出is_POD_type特性, 转交给__uninitialized_copy_aux()处理
*/
template <class _InputIter, class _ForwardIter, class _Tp>
inline _ForwardIter
__uninitialized_copy(_InputIter __first, _InputIter __last,
                     _ForwardIter __result, _Tp*)
{
  typedef typename __type_traits<_Tp>::is_POD_type _Is_POD;
  return __uninitialized_copy_aux(__first, __last, __result, _Is_POD());  // 萃取型别Tp的is_POD_type特性, 用于判断Tp是否为POD类型
}

// Valid if copy construction is equivalent to assignment, and if the
//  destructor is trivial.
// 如果是POD型别, 就dispatch到这里.
// 迭代器所指元素是POD型别, 直接调用copy算法, 将元素从源区间拷贝到目的区间.
template <class _InputIter, class _ForwardIter>
inline _ForwardIter
__uninitialized_copy_aux(_InputIter __first, _InputIter __last,
                         _ForwardIter __result,
                         __true_type)
{
  return copy(__first, __last, __result); // 调用std::copy算法, 由于只是简单内存拷贝, 不会发生异常
}

// 如果不是POD型别, 就dispatch到这里
// 迭代器所指元素不是POD型别, 就逐个调用construct函数, 利用源区间元素来构造目的区间元素.
template <class _InputIter, class _ForwardIter>
_ForwardIter
__uninitialized_copy_aux(_InputIter __first, _InputIter __last,
                         _ForwardIter __result,
                         __false_type)
{
  _ForwardIter __cur = __result;
  __STL_TRY {
    for ( ; __first != __last; ++__first, ++__cur)
      _Construct(&*__cur, *__first);       // 调用全局construct()在cur所指内存上, 利用first构造cur所指原生类型的对象
    return __cur;
  }
  __STL_UNWIND(_Destroy(__result, __cur)); // 如果construct()调用构造函数构造对象过程抛出异常, 就析构已构造区间[result, cur)所指对象, 并且捕获异常并重新抛出.
}


// 附
#   define __STL_TRY try                                      // try 语句块
#   define __STL_UNWIND(action) catch(...) { action; throw; } // 捕获异常后先执行action, 再re-throw

template <class _Tp>
inline void _Destroy(_Tp* __pointer) { // 调用pointer所指对象的析构函数
  __pointer->~_Tp();
}

__uninitialized_copy_aux(first, last, result, __false_type)中,对“commit or rollback”的实现非常精妙:通过在迭代器所指内存上构造对象,如果抛出异常,就会终止循环并且不会正常return,而是在函数末尾通过__STL_UNWIND()不会异常,并通过_Destroy(__result, __cur)来对已经构造对象的迭代器区间[result, cur)进行析构。

POD型别

POD型别是指Plain Old Data,即标量型别(scalar types)或传统的C struct型别。参见之前这篇文章 C++ POD类型(C++ POD类型)。
一个POD类型,是指平凡的类型。而一个平凡的类或结构体符合以下定义:
1)拥有平凡的默认构造函数(trivial constructor)和析构函数(trivial destructor);

2)拥有平凡的拷贝构造函数(trivial copy constructor)和移动构造函数(trivial move constructor);

3)拥有平凡的拷贝赋值运算符(trivial assignment operator)和移动赋值运算符(trivial move operator);

4)不能包含virtual函数和virtual基类。

uninitialized_fill

如果源迭代器区间[first, last)每个迭代器都指向未初始化的内存,那么uninitialized_fill() 会在该范围内产生x(上式第三参数)的复制品。
i.e. uninitialized_fill() 会针对操作范围内的每个迭代器i,调用construct(&*i, x),在i所指之处产生x的复制品。

同uninitialized_copy(),uninitialized_fill()必须具备“commit or rollback”语义。i.e. 要么产生出所有必要元素,要么不产生任何元素。如果有任何一个copy constructor抛出异常(exception),uninitialized_fill()必须能够将已产生的所有元素析构掉。

// 要填充的迭代器区间[first, last), 要填充的值x
// 根据first所指对象value type是否为POD类型, 决定是否调用construct()构造对象
template <class _ForwardIter, class _Tp>
inline void uninitialized_fill(_ForwardIter __first,
                               _ForwardIter __last,
                               const _Tp& __x)
{
  __uninitialized_fill(__first, __last, __x, __VALUE_TYPE(__first)); // 萃取出first的value type
}

// 根据Tp1的is_POD_type特性, 将任务dispatch至不同版本的__uninitialized_fill_aux()
template <class _ForwardIter, class _Tp, class _Tp1>
inline void __uninitialized_fill(_ForwardIter __first,
                                 _ForwardIter __last, const _Tp& __x, _Tp1*)
{
  typedef typename __type_traits<_Tp1>::is_POD_type _Is_POD; // 萃取出Tp1的is_POD_type特性
  __uninitialized_fill_aux(__first, __last, __x, _Is_POD());
                   
}

// Valid if copy construction is equivalent to assignment, and if the
// destructor is trivial.
// 如果first所指对象是POD类型, 就会dispatch至该函数
template <class _ForwardIter, class _Tp>
inline void
__uninitialized_fill_aux(_ForwardIter __first, _ForwardIter __last,
                         const _Tp& __x, __true_type)
{
  fill(__first, __last, __x); // std::fill算法. 将x拷贝至目标区间[first, last). 由于是POD类型, 并未调用construct()构造对象, 不会抛出异常
}

// 如果first所指对象不是POD类型, 就会dispatch至该函数
template <class _ForwardIter, class _Tp>
void
__uninitialized_fill_aux(_ForwardIter __first, _ForwardIter __last,
                         const _Tp& __x, __false_type)
{
  _ForwardIter __cur = __first;
  __STL_TRY {
    for ( ; __cur != __last; ++__cur)
      _Construct(&*__cur, __x); // 对cur所指对象空间调用copy constructor, 以x值拷贝构造新对象
  }
  __STL_UNWIND(_Destroy(__first, __cur)); // commit or rollback 精髓: 发生异常时, 捕获异常, 析构[first, cur)所有已构造元素, 之后re-throw异常
}

uninitialized_fill_n

uninitialized_fill()以迭代器区间[first, last)的形式,让我们以x值为参数为该区间填充对象。uninitialized_fill_n()功能类似,不过提供的不是迭代器区间,而是(first, n)(迭代器起始位置,长度)的形式,可以转换为迭代器区间[first, first+n)。

uninitialized_fill_n() 也有“commit or rollback”语义:要么产生所有必要元素,否则就不产生任何元素。如果任何一个copy ctor抛出异常(exception),uninitialized_fill_n() 必须析构已产生的所有元素。

// 拷贝x值到未初始化的区域(first, n)(起始迭代器, 长度), 对应迭代器区间[first, first+n)
// 会根据first的value type, 决定是否调用construct()构造对象
template <class _ForwardIter, class _Size, class _Tp>
inline _ForwardIter
uninitialized_fill_n(_ForwardIter __first, _Size __n, const _Tp& __x)
{
  return __uninitialized_fill_n(__first, __n, __x, __VALUE_TYPE(__first)); // 萃取出first的value type特性
}

// 根据Tp1的is_POD_type特性, dispatch至不同版本的__uninitialized_fill_n_aux()处理
template <class _ForwardIter, class _Size, class _Tp, class _Tp1>
inline _ForwardIter
__uninitialized_fill_n(_ForwardIter __first, _Size __n, const _Tp& __x, _Tp1*)
{
  typedef typename __type_traits<_Tp1>::is_POD_type _Is_POD; // __type_traits<>技法, 萃取POD特性
  return __uninitialized_fill_n_aux(__first, __n, __x, _Is_POD());
}

// Valid if copy construction is equivalent to assignment, and if the
//  destructor is trivial.
// 如果first所指对象是POD类型, 就会dispatch至此
template <class _ForwardIter, class _Size, class _Tp>
inline _ForwardIter
__uninitialized_fill_n_aux(_ForwardIter __first, _Size __n,
                           const _Tp& __x, __true_type)
{
  return fill_n(__first, __n, __x); // 将x拷贝至first为起点, 长度为n的区间
}

// 如果first所指对象不是POD类型, 就会dispatch至此
template <class _ForwardIter, class _Size, class _Tp>
_ForwardIter
__uninitialized_fill_n_aux(_ForwardIter __first, _Size __n,
                           const _Tp& __x, __false_type)
{
  _ForwardIter __cur = __first;
  __STL_TRY {
    for ( ; __n > 0; --__n, ++__cur)
      _Construct(&*__cur, __x); // 对cur所指对象空间调用copy constructor, 以x值拷贝构造新对象
    return __cur;
  }
  __STL_UNWIND(_Destroy(__first, __cur)); // commit or rollback精髓: 发生异常时, 捕获异常, 析构[first, cur)所有已构造元素, 之后re-throw异常
}
posted @ 2022-05-05 08:54  明明1109  阅读(763)  评论(0编辑  收藏  举报