日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

C++ Standard Stl -- SGI STL源码学习笔记(07) stl_vector 与 一些问题的细化 3 resize函数剖析...

發(fā)布時間:2023/12/20 c/c++ 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C++ Standard Stl -- SGI STL源码学习笔记(07) stl_vector 与 一些问题的细化 3 resize函数剖析... 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

  前面在介紹push_back函數(shù)的時候有說到placement new的用法.前面說的很簡單.這幾天處理一些其他的事情,直到昨天下午

才有時間看源碼,順便安靜的看一下書. 其中我又看到了掛關(guān)于placement new的介紹,那么就在文章開始之前先說一下這個問題.

  placement new又稱"定為new"(我想這純粹是翻譯過來的意思.),當(dāng)在禁止使用Heap分配的時候,也就是聲明對象的構(gòu)造函數(shù)

為private的時候,我們不能調(diào)用構(gòu)造函數(shù)去構(gòu)造一個對象,這個時候就可以使用placement new. 前一段時間我在閱讀sig stl源碼的

時候也看到了stl容器對于placement new的使用.

  placement new 的作用是在一個特定的位置放置一個對象,所以不需要調(diào)用構(gòu)造函數(shù),但是卻和構(gòu)造函數(shù)的作用相同. 

需要注意的是placement new并不分配實際的存儲區(qū)域, 而是返回一個指向已經(jīng)分配好空間的指針.所以不要對其執(zhí)行delete操作.

  但是確實創(chuàng)建了一個對象,如果想要銷毀對象,那么需要調(diào)用嗯對象的析構(gòu)函數(shù).

?????? placement大多都是使用在容器技術(shù)中,而且是高級技術(shù),也通常用來隱藏技術(shù)實現(xiàn)的細(xì)節(jié),這里只做簡單了解.

?

前面很多文章都是介紹stl_vector,這篇文章會介紹vector的resize函數(shù),并作為結(jié)尾. 先看一下resize函數(shù)的源碼:

?

void resize(size_type __new_size, const _Tp& __x) {if (__new_size < size()) erase(begin() + __new_size, end());    // 擦除begin()+__new_size -> end()之間的元素elseinsert(end(), __new_size - size(), __x);}void resize(size_type __new_size) { resize(__new_size, _Tp()); } // 這和上面一樣,只不過是提供默認(rèn)的參數(shù).

?

1. 首先第一點很容易看得出,erase函數(shù)執(zhí)行的是擦除工作,并不能分配內(nèi)存.

2. insert在__new_size >= size()的時候會執(zhí)行內(nèi)存的重新分配.

?

先看一下erase的源碼:

iterator erase(iterator __first, iterator __last) {iterator __i = copy(__last, _M_finish, __first);destroy(__i, _M_finish);_M_finish = _M_finish - (__last - __first);return __first;}

?跟著copy的源碼走下去,最終會看到最后的實現(xiàn)是:(__copy_trivial)

template <class _Tp> inline _Tp* __copy_trivial(const _Tp* __first, const _Tp* __last, _Tp* __result) {memmove(__result, __first, sizeof(_Tp) * (__last - __first));return __result + (__last - __first); }

 其實上面的erase在resize函數(shù)中使用的時候是不會執(zhí)行copy函數(shù)的.因為end() == _M_finish.所以只需要將

? ?begin() + __new_size -> _M_finish之間的元素都銷毀.destory源碼如下:

inline void _Destroy(_Tp* __pointer) {__pointer->~_Tp(); // 這里不是使用delete,而是調(diào)用元素對象本身的析構(gòu)函數(shù). }

  找了很多層,執(zhí)行很多跳轉(zhuǎn)最后才找到上面的最終源碼,為什么stl要這么麻煩? 因為stl對于容器的內(nèi)存是使用placement new

  前面說過,所以需要調(diào)用對象本身的析構(gòu)函數(shù)來完成工作.

?

3. 接下來,我們看看,如果__new_size > size()執(zhí)行insert函數(shù)的情況.insert函數(shù)源碼如下:

void insert (iterator __pos, size_type __n, const _Tp& __x){ _M_fill_insert(__pos, __n, __x); }

  下面調(diào)用到_M_fill_insert函數(shù),這個函數(shù)在前面的文章中有講解過.不過當(dāng)時只講解了該函數(shù)的一部分. 本文來看看上半部分. 

template <class _Tp, class _Alloc> void vector<_Tp, _Alloc>::_M_fill_insert(iterator __position, size_type __n, const _Tp& __x) {if (__n != 0) { // __new_size - size() > 0 if (size_type(_M_end_of_storage - _M_finish) >= __n) {_Tp __x_copy = __x;const size_type __elems_after = _M_finish - __position;iterator __old_finish = _M_finish;if (__elems_after > __n) {uninitialized_copy(_M_finish - __n, _M_finish, _M_finish);_M_finish += __n;copy_backward(__position, __old_finish - __n, __old_finish);fill(__position, __position + __n, __x_copy);}else {uninitialized_fill_n(_M_finish, __n - __elems_after, __x_copy);_M_finish += __n - __elems_after;uninitialized_copy(__position, __old_finish, _M_finish);_M_finish += __elems_after;fill(__position, __old_finish, __x_copy);}}

  在上面的注釋中,只會調(diào)用_M_fill_insert函數(shù)的前部分,而后半部分的源碼在前面講解過了.

    情況又作為兩種子情況劃分:

      1. size_type(_M_end_of_storage - _M_finish) > ? __n(__n = __new_size - size() )

      2.?size_type(_M_end_of_storage - _M_finish) <= __n(__n = __new_size - size())

  

對于這兩種情況該如何解釋呢?如何去理解. _M_end_of_storage代表著存儲能力,_M_finish代表著當(dāng)前的已存儲位置, size()

返回的不是容器的存儲能力,而是當(dāng)前已經(jīng)存儲的元素個數(shù). 理解了這些,上面的兩種情況就比較好理解了. ?

  

  大的前提條件是:需要resize的新長度已經(jīng)大于已經(jīng)存儲的長度.

    對于1情況: __new_size在_M_end_of_storage內(nèi). 也就是在存儲能力內(nèi).

    對于2情況: 則不在存儲能力范圍內(nèi)了,需要擴大存儲能力,也就是擴大存儲內(nèi)存單元.

好吧,我們?nèi)タ纯碨TL源碼是如何處理的.

?

對于情況1:

if (size_type(_M_end_of_storage - _M_finish) >= __n) {_Tp __x_copy = __x;const size_type __elems_after = _M_finish - __position;iterator __old_finish = _M_finish;if (__elems_after > __n) { // 這下面的語句不會執(zhí)行,因為_M_finish = _position uninitialized_copy(_M_finish - __n, _M_finish, _M_finish);_M_finish += __n;copy_backward(__position, __old_finish - __n, __old_finish);fill(__position, __position + __n, __x_copy);}else { // 從這里開始執(zhí)行.uninitialized_fill_n(_M_finish, __n - __elems_after, __x_copy);_M_finish += __n - __elems_after;                   // _M_finish 向后移動__n-1uninitialized_copy(__position, __old_finish, _M_finish); _M_finish += __elems_after;fill(__position, __old_finish, __x_copy);}}

  我們假設(shè)容器中存儲的元素類型不是POD,所以追溯源碼就可以找到這里:

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);return __cur;}__STL_UNWIND(_Destroy(__first, __cur)); }

  很容易看得出,就是從_M_finish到后面的_n個位置都使用placement new初始化元素為__x.

  接著查看uninitialized_copy的源碼可以發(fā)現(xiàn)并不會執(zhí)行: 和前面一樣,元素類型同樣不是POD

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) // _position == old_position , 所以不會執(zhí)行._Construct(&*__cur, *__first);return __cur;}__STL_UNWIND(_Destroy(__result, __cur)); }

  

?  好吧,對于情況1的分析,已經(jīng)很清楚了.下面看看情況2.

const size_type __old_size = size(); const size_type __len = __old_size + max(__old_size, __n); // 這里不是簡單的擴張兩倍.因為不知道new_size和old_size之間的關(guān)系.iterator __new_start = _M_allocate(__len);           // 重新申請內(nèi)存iterator __new_finish = __new_start;              // 初始化__STL_TRY {__new_finish = uninitialized_copy(_M_start, __position, __new_start); // 將原數(shù)組中_M_start -> _M_finish之間的元素復(fù)制到新數(shù)組中__new_finish = uninitialized_fill_n(__new_finish, __n, __x);       // 初始化_new_finish -> _new_finish + __n 之間的元素__new_finish= uninitialized_copy(__position, _M_finish, __new_finish);

  情況2執(zhí)行的代碼相比較情況1要少的多,盡管要執(zhí)行新的內(nèi)存分配. 在上面的源碼注釋中,我也注明了,這里不是簡單的擴張為原來的兩倍.

  為什么要這么做呢? 原因其實很簡單,因為resize時候,__new_size和size()之間的關(guān)系是不知道的,有可能是三倍四倍,也有可能是二倍,

  或者說是介于這些數(shù)字之間,所以不應(yīng)該用一個確切的數(shù)字來決定.

  不知道會不會有人問一個問題: ?關(guān)于內(nèi)存擴張_len的確定為什么和_M_end_of_storage沒有關(guān)系了,就是為什么和存儲能力沒有關(guān)系了。

  而是和size()有關(guān)系. ?額,答案是這個樣子的.在前面分析兩種情況的時候就說明了,情況2是已經(jīng)不在存儲范圍內(nèi)了,所以需要結(jié)合這些基本情況

  聯(lián)系在一起考慮.

?

最后對于情況1,情況2都執(zhí)行相同的源碼:  

    destroy(_M_start, _M_finish);_M_deallocate(_M_start, _M_end_of_storage - _M_start);_M_start = __new_start;_M_finish = __new_finish;_M_end_of_storage = __new_start + __len;

  執(zhí)行一些初始化和清理工作,收尾.

  

? ? ? 到這里,關(guān)于resize函數(shù)的介紹就都結(jié)束了.?

?

?

? 小結(jié):

    STL中容器對于元素的存儲在底層使用的都是數(shù)組,而實現(xiàn)數(shù)據(jù)結(jié)構(gòu)都是使用_M_start,_M_finish,_M_end_of_storage.

    STL中的函數(shù)提供的通用性是很好的,而且源碼的設(shè)計與數(shù)據(jù)結(jié)構(gòu)的實現(xiàn)很精巧,同時也是很復(fù)雜的.?

?

?

?

?

?

?

?

  

?

?

?

 

  

?

?

轉(zhuǎn)載于:https://www.cnblogs.com/respawn/archive/2012/08/03/2621125.html

總結(jié)

以上是生活随笔為你收集整理的C++ Standard Stl -- SGI STL源码学习笔记(07) stl_vector 与 一些问题的细化 3 resize函数剖析...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。