STLのソースコード解析 - 基本的なツールを扱うアルゴリズム#1のメモリ

  uninitialized_copy、uninitialized_fill、uninitialized_fill_n:我々はシーケンスコンテナを勉強するとき、私たちは、多くの場合、これらの3つの機能を発生します。その時、私たちは、ただ単にそれらが実装されている方法として、我々は一番下に取得していない、これらの関数を知っています。このセクションでは、これらの機能のマスクを脱ぐために時間がかかる、彼らはその側面を見ることが不明です。

  • uninitialized_copy

 

 

  関数のシグネチャ:

1つのテンプレート< クラス InputIterator、クラス ForwardIterator>
 2  インラインForwardIterator
 3  uninitialized_copy(InputIteratorまず、InputIterator最後に、
 4      ForwardIterator結果)

  この関数は一般に、オブジェクト分離のメモリ構成の構造、例えばベクターの場合に使用され、要素のベクトルの数は、一般に、その後、所望行うuninitialized_copyスペア空間新しい要素に入れより多くのスペースを配置します。換言すれば、その結果、各反復点範囲初期化されていない領域内の結果+(最後最初))、uninitialized_copyは()[最初から、コピーコンストラクタを使用する[として出力先、最後)は、それぞれ出力範囲内、目標範囲の単一のコピーを生成します。反復子iは各入力範囲に対して、関数呼び出しはiは、出力に入れ*、構築物は、新しい内部配置が使用され、レプリカを生成し(結果は+())、* iは第(*&)を構築します相対位置の範囲。

  出典:

1つのテンプレート< クラス InputIterator、クラス ForwardIterator>
 2  インラインForwardIterator
 3  uninitialized_copy(InputIteratorまず、InputIterator最後に、
 4      ForwardIterator結果){
 5      リターン__uninitialized_copy(最初、最後、その結果、VALUE_TYPE(結果))。
6      // 以上、利用VALUE_TYPE()取出第的値型。
7 }

  この関数内で、オブジェクトの意義の範囲内でイテレータ型にアクセスし、次のレベルの関数__uninitialized_copyに渡し、我々はこれが__uninitialized_copyを支持しているものであるかどうかを確認するためにトレースし続けます。

1つのテンプレート< クラス InputIterator、クラス ForwardIterator、クラス T>
 2  インラインForwardIterator
 3。 __uninitialized_copy(InputIteratorまず、InputIterator最後に、
 4。      ForwardIterator結果、T * ){
 5      のtypedef型名__type_traits <T> :: is_POD_type is_POD;         // type_traits機構を使用し、イテレータ型PODタイプが参照される分析
。6      戻り、__uninitialized_copy_aux(最初、最後、その結果、is_POD())は、
 7。     // 、上記is_PODを(使用する試み)を得られた結果は、コンパイラがオーバーレイとして導出することを可能にする
8 }

  __uninitialized_copy(パラメータ導出がオーバーロード)の判断をするためにイテレータ型is_POD情報の意味の範囲内で取得しますが、POD型のものではありません、関数内で判断をしなかったが、コンパイラに。PDOは、どのようなタイプですか?データオールドPODプレーンことを意味する、すなわち、実質的に従来型のCまたは構造。オブジェクトタイプPODは必ずしも/割り当てコンストラクタ臨時建設/デフォルトコンストラクタ/コピーコンストラクタを持っています。

  POD型の場合は、最も効率的な複製技術を使用しますが、それはまだ、この層の機能には表示されませんが、コピー機能インチ

1つのテンプレート< クラス InputIterator、クラス ForwardIterator>
 2  インラインForwardIterator
 3  __uninitialized_copy_aux(InputIteratorまず、InputIterator最後に、
 4      ForwardIterator結果、
 5      __true_type){
 6      戻りコピー(最初、最後、結果)。// 呼叫STL算法コピー()
7 }

基本的なSTLアルゴリズムの一つ--copy

  代入演算子を使用していない、またはコンストラクタをコピー(元とコピー)が、特定のオブジェクトの種類は自明(トリビアル)代入演算子ある場合、ダイレクト・メモリ・コピー(例えば、Cとして使用することができませんコピー動作よりMEMMOVE機能やmemcoy)、多くの時間を節約することができるようになります。この目的のために、コピーアルゴリズムは、複製効率を向上させる技術をプログラミング関数のオーバーロード、特徴の種類(type_traits)、及び他の専門分野を含む様々なアプローチを使用します。

 

   関数のシグネチャ:

1テンプレート< クラス InputIterator、クラス OutputIterator>
 2  インラインOutputIteratorコピー(InputIteratorまず、InputIterator最後に、
 3      OutputIterator結果)

  この関数は、コピー出力区間[結果、結果+(最後の最初)に最初の区間[内の全ての入力要素、最後))、すなわち、それは実行割り当て*結果= *まず、*(結果+ 1)= *(最初+ 1)、...というように。Iteratorを返します。結果+(最後の最初)。コピー機能は、テンプレート引数は非常に緩んでいる必要があり。入力範囲はInputIteratorの最低レベルであることができ、出力範囲はOuputIteratorの最も低いレベルです。これは、コンテナの任意のセクションのいずれかのセクションにコピーし、このアルゴリズムでは、任意の容器の範囲で任意のコンテンツを、使用できることを意味します。あなたはそのコピーが前進している見ることができます(また、別の関数を学習した後、推進力をリバース)。

  出典:上記のソースコードを、コピー全体アルゴリズムの文脈における図では、簡単に理解するために見ることができます

1 //完全泛化版本
2 template <class InputIterator, class OutputIterator>
3 inline OutputIterator copy(InputIterator first, InputIterator last,
4     OutputIterator result)
5 {
6     return __copy_dispatch<InputIterator, OutputIterator>()(first, last, result);
7 }

  除了这个完全泛化版本,还提供两个特化版本,针对原生指针const char* 和 const wchar_t*,可以使用内存直接拷贝的操作:

 1 inline char* copy(const char* first, const char* last, char* result) {
 2     memmove(result, first, last - first);
 3     return result + (last - first);
 4 }
 5 
 6 inline wchar_t* copy(const wchar_t* first, const wchar_t* last,
 7     wchar_t* result) {
 8     memmove(result, first, sizeof(wchar_t) * (last - first));
 9     return result + (last - first);
10 }

  为什么说memmove函数效率很高呢?因为该函数是直接按字节拷贝,简单地讲就是纯复制,不用考虑其他诸如构造、浅复制、深复制等因素,所以效率很高。所以如果允许的情况下,最好就就用这个函数和memcpy函数。至于memmove和memcpy有什么区别,搜一搜就有了。

  copy函数的完全泛化版本调用了一个__copy_dispatch函数,这个函数有一个完全泛化版本和两个偏特化版本:

1 template <class InputIterator, class OutputIterator>
2 struct __copy_dispatch
3 {
4     //__copy_dispatch是一个结构体,但定义了operator()函数,即当使用__copy_dispatch(),调用的是__copy_dispatch()的函数对象
5     OutputIterator operator()(InputIterator first, InputIterator last,
6         OutputIterator result) {
7         return __copy(first, last, result, iterator_category(first));        //获取迭代器的类型,并根据编译器的参数推导机制选择合适的重载版本
8     }
9 };

  而这两个偏特化版本针对的是模板参数为指针类型的,即传进来的是int*而非int这种情况:

 1 template <class T>
 2 struct __copy_dispatch<T*, T*>
 3 {
 4     T* operator()(T* first, T* last, T* result) {
 5         typedef typename __type_traits<T>::has_trivial_assignment_operator t;        //获取类型是否具有平凡的赋值运算符
 6         return __copy_t(first, last, result, t());
 7     }
 8 };
 9 
10 template <class T>
11 struct __copy_dispatch<const T*, T*>
12 {
13     T* operator()(const T* first, const T* last, T* result) {
14         typedef typename __type_traits<T>::has_trivial_assignment_operator t;
15         return __copy_t(first, last, result, t());
16     }
17 };

  这里兵分两路。首先__copy_dispatch的完全泛化版本会根据迭代器类型选择调用合适的__copy函数,为的是不同种类的迭代器所使用的循环条件不同,而效率也会不同。如果是最低级别的InputIterator,说明迭代器只具有逐次递增的功能,不具备随机访问的功能,所以在判断是否到达输入区间尾部这个问题上,需要每次递增后都要进行一次判断,效率较低:

1 template <class InputIterator, class OutputIterator>
2 inline OutputIterator __copy(InputIterator first, InputIterator last,
3     OutputIterator result, input_iterator_tag)
4 {
5     for (; first != last; ++result, ++first)    //每轮循环开始前都要进行一次fisrt是否等于last的判断,效率慢
6         *result = *first;
7     return result;
8 }

  如果是级别最高的Random Access Iterator,则具有了随机存取的能力,就可以利用它提供的operator-()计算出输出区间的大小n,然后以n决定循环的次数,这比两个迭代器作比较在效率上快多了。

 1 template <class RandomAccessIterator, class OutputIterator>
 2 inline OutputIterator
 3 __copy(RandomAccessIterator first, RandomAccessIterator last,
 4     OutputIterator result, random_access_iterator_tag)
 5 {
 6     return __copy_d(first, last, result, distance_type(first));
 7 }
 8 
 9 template <class RandomAccessIterator, class OutputIterator, class Distance>
10 inline OutputIterator
11 __copy_d(RandomAccessIterator first, RandomAccessIterator last,
12     OutputIterator result, Distance*)
13 {
14     for (Distance n = last - first; n > 0; --n, ++result, ++first) //Distance其实就是distance_type
15         *result = *first;
16     return result;
17 }

   这是__copy_dispatch的完全泛化版本。现在回到兵分两路之前,__copy_dispatch具有两个偏特化版本,针对的是参数为原生指针类型和const原生指针类型,从这两个函数的主体中能看出,它想在“参数为原生指针类型”的前提下,探测其指针所指的类型是否具有平凡的赋值操作符。如果具有平凡的赋值运算符(即并无重载其赋值操作符),那么复制操作就可以不通过赋值操作符来进行,可以直接以组快速的内存对拷方式(memmove())完成。源码使用了我们之前学过的__type_traits<>编程技巧来侦测某个类型是否具有平凡的赋值操作符。

 1 template <class T>
 2 inline T* __copy_t(const T* first, const T* last, T* result, __true_type) {
 3     //__true_type,拥有平凡赋值操作符(trivial assignment operator),使用内存对拷方式,效率最高
 4     memmove(result, first, sizeof(T) * (last - first));
 5     return result + (last - first);
 6 }
 7 
 8 template <class T>
 9 inline T* __copy_t(const T* first, const T* last, T* result, __false_type) {
10     //__false_type,拥有自定义的赋值操作符(non-trivial assignment operator),使用随机迭代器版本的复制操作
11     return __copy_d(first, last, result, (ptrdiff_t*)0);
12 }

  至此,我们终于走到了uninitialized_copy的尽头,可谓是廓然开朗。

 

  • uninitialized_fill

  函数签名:

1 template <class ForwardIterator, class T>
2 inline void uninitialized_fill(ForwardIterator first, ForwardIterator last,
3     const T& x)

  如果[ first, last ) 范围内每个迭代器都指向未初始化的内存,那么该函数会在该范围内产生x的复制品。换句话说,该函数会针对操作范围内的每个迭代器 i ,调用construct(&*i, x),在 i 所指之处产生x的复制品。

  源码:

1 template <class ForwardIterator, class T>
2 inline void uninitialized_fill(ForwardIterator first, ForwardIterator last,
3     const T& x) {
4     __uninitialized_fill(first, last, x, value_type(first));
5 }

  与uninitilized_copy一样,先获取迭代器所指的类型,然后再下一级函数中,判断其是否是POD类型:

1 template <class ForwardIterator, class T, class T1>
2 inline void __uninitialized_fill(ForwardIterator first, ForwardIterator last,
3     const T& x, T1*) {
4     typedef typename __type_traits<T1>::is_POD_type is_POD;
5     __uninitialized_fill_aux(first, last, x, is_POD());
6 }

  再根据是否是POD类型,调用不同版本的__uninitialized_fill_aux。

  如果是POD类型,则具有平凡的赋值操作符,就会进入到fill函数:

1 template <class ForwardIterator, class T>
2 inline void
3 __uninitialized_fill_aux(ForwardIterator first, ForwardIterator last,
4     const T& x, __true_type)
5 {
6     fill(first, last, x); // 呼叫 STL 算法 fill()
7 }

STL基本算法之二——fill()

  fill函数就比copy简单多了,我们只会在类型为POD时才会调用该函数,所以该函数直接使用平凡的赋值操作符进行初值填写:

1 template <class ForwardIterator, class T>
2 void fill(ForwardIterator first, ForwardIterator last, const T& value) {
3     for (; first != last; ++first)    // 遍历整个输入区间
4         *first = value;
5 }

  如果不是POD类型,说明具有自己的赋值操作符,那么就不会使用赋值操作,而是直接在迭代器所指空间直接构造一个value:

 1 template <class ForwardIterator, class T>
 2 void
 3 __uninitialized_fill_aux(ForwardIterator first, ForwardIterator last,
 4     const T& x, __false_type)
 5 {
 6     ForwardIterator cur = first;
 7     __STL_TRY{
 8         for (; cur != last; ++cur)
 9         construct(&*cur, x); // 必须一个一个构造,无法批量进行
10     }
11     __STL_UNWIND(destroy(first, cur));
12 }

 

  • uninitialized_fill_n

  函数签名:

1 template <class ForwardIterator, class Size, class T, class T1>
2 inline ForwardIterator __uninitialized_fill_n(ForwardIterator first, Size n,
3     const T& x, T1*) 

  如果[ first, first+n ),范围内的每一个迭代器都指向未初始化的空间,那么该函数会调用拷贝构造函数,在该范围内产生x的复制品。也就是说,对于[ first, first+n ),范围内的每个迭代器 i ,该函数会调用construct(&*i, x),在对应位置产生x的复制品。

  源码:

1 template <class ForwardIterator, class Size, class T, class T1>
2 inline ForwardIterator __uninitialized_fill_n(ForwardIterator first, Size n,
3     const T& x, T1*) {
4     typedef typename __type_traits<T1>::is_POD_type is_POD;
5     return __uninitialized_fill_n_aux(first, n, x, is_POD());
6 }

  有一说一,其实与上述两个函数类似,POD类型决定其使用什么样的复制手段,不是POD类型就直接构造,是就直接赋值。

1 template <class ForwardIterator, class Size, class T, class T1>
2 inline ForwardIterator __uninitialized_fill_n(ForwardIterator first, Size n,
3     const T& x, T1*) {
4     typedef typename __type_traits<T1>::is_POD_type is_POD;
5     return __uninitialized_fill_n_aux(first, n, x, is_POD());
6 }

  不同的是,如果是POD类型,调用的是STL算法里面的fill_n函数:

1 template <class ForwardIterator, class Size, class T>
2 inline ForwardIterator
3 __uninitialized_fill_n_aux(ForwardIterator first, Size n,
4  const T& x, __true_type) {
5  return fill_n(first, n, x);
6 }

STL基本算法之三——fill_n()

  将[ first, last ) 内的前n个元素改填新值,返回的迭代器指向被填入的最后一个元素的下一位置:

1 template <class OutputIterator, class Size, class T>
2 OutputIterator fill_n(OutputIterator first, Size n, const T& value) {
3     for (; n > 0; --n, ++first)        // 经过n个元素
4         *first = value;    // 注意,assignment 是复写(overwrite)而不是安插(insert)
5     return first;
6 }

  而对于非POD类型则采取最保险的做法:

 1 template <class ForwardIterator, class Size, class T>
 2 ForwardIterator
 3 __uninitialized_fill_n_aux(ForwardIterator first, Size n,
 4     const T& x, __false_type) {
 5     ForwardIterator cur = first;
 6     __STL_TRY{
 7         for (; n > 0; --n, ++cur)
 8         construct(&*cur, x);
 9         return cur;
10     }
11     __STL_UNWIND(destroy(first, cur));
12 }

 

おすすめ

転載: www.cnblogs.com/MisakiJH/p/11748321.html