• 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异常
    }
    
  • 相关阅读:
    【洛谷P1558】色板游戏
    【SCOI2010】序列操作
    Windows下MySQL下载安装、配置与使用
    Redis和MySQL数据同步及Redis使用场景
    python的设计模式之MVC模式
    python 面试真题
    python调用C语言
    一次跨域请求出现 OPTIONS 请求的问题及解决方法
    服务器宕机问题
    页面静态化
  • 原文地址:https://www.cnblogs.com/fortunely/p/16223417.html
Copyright © 2020-2023  润新知