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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

string类的erase函数属于stl吗_探索STL容器:vector

發布時間:2024/7/23 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 string类的erase函数属于stl吗_探索STL容器:vector 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

用了這么久的 vector ,今天終于有時間來看下STL的實現源碼了,開心?~


最近幾個月在刷 leetcode ,用的較多的數據結構就是STL里面的 vector 了,相比較于直接的 array 數組,它具備了靈活地根據需求去分配管理內存,用戶只管往里面扔東西,拿東西,而不用費心費力去解決C++里面的動態內存問題。

那么大致猜想一下,要實現一個這樣的容器,不難想出在 vector 中至少存在這樣三個私有成員:head(指向列表第一個元素位置), tail(指向列表最后一個元素位置), size(容器大小) ,

打開源碼看看,得到驗證:

template<typename?_Tp,?typename?_Alloc>
struct?_Vector_base?{
????......
????struct?_Vector_impl_data?{
????????pointer?_M_start;??//?指向容器中的第一個元素,是一個指針,指針類型為?Tp?所示類型
????????pointer?_M_finish;?//?指向容器最后一個元素,也是一個指針
????????pointer?_M_end_of_storage;?//?指向容器最后的位置
????????......
????}
}
template<typename?_Tp,?typename?_Alloc?=?std::allocator<_tp>?>
class?vector?:?protected?_Vector_base<_tp>?{typedef?_Vector_base<_tp>?_Base;?//?如上?_Vector_base?所示,是一個基礎實現
????......public:typedef?_Tp?value_type;??//?數據類型typedef?typename?_Base::pointer?pointer;?//?全局數據指針
????......
}

總之就是一層套一層,封裝了一個又一個,來完成對數據的抽象。但是最后的接口是放在 vector 上放開的。

vector 支持動態內存分配,支持隨機存取訪問,因此在數據訪問上具備了指針有的特性。那么它在源碼上是怎么實現的呢?首先來看它的接口(一部分常用接口)都有哪些:

**注:**雖然包含 頭文件就可以用 vector ,但是它的實現源碼其實是在 bits/stl_vector.h 下的,而且下面的程序刪掉了一些注釋類、系統判斷類函數。

template<typename?_Tp,?typename?_Alloc?=?std::allocator<_tp>?>
class?vector?:?protected?_Vector_base<_tp>?{
????...//?獲取指向第一個元素的指針
????iterator?begin()?{?return?iterator(this->_M_impl._M_start);?}//?獲取指向容器最后一個可存放位置的下一個位置iterator?end(){?return?iterator(this->_M_impl._M_finish);?}//?獲取容器當前存放了多少元素size_type?size()?const?{?return?size_type(this->_M_impl._M_finish?-?this->_M_impl._M_start);?
????}//?對容器進行重新分配大小void?resize(size_type?__new_size)?{if?(__new_size?>?size())
????????????_M_default_append(__new_size?-?size());else?if?(__new_size?????????????_M_erase_at_end(this->_M_impl._M_start?+?__new_size);
????}//?獲取容器自身大小size_type?capacity()?const??{return?size_type(this->_M_impl._M_end_of_storage
?????????????????????????-?this->_M_impl._M_start);
????}//?看看容器當前是否為空bool?empty()?const??{?return?begin()?==?end();?}//?獲取一個能夠對返回對象讀寫操作的引用類型,其實就是指針
????reference?operator[](size_type?__n)??{return?*(this->_M_impl._M_start?+?__n);
????}protected://?檢查訪問是否合法void?_M_range_check(size_type?__n)?const?{if?(__n?>=?this->size())
????????????__throw_out_of_range_fmt(__N("vector::_M_range_check:?__n?""(which?is?%zu)?>=?this->size()?""(which?is?%zu)"),
?????????????????????????????????????__n,?this->size());
????}public://?跟?[]?操作符差不多,只不過多了一層范圍檢查reference?at(size_type?__n)?{
????????_M_range_check(__n);return?(*this)[__n];
????}//?獲取能夠對頭部元素讀寫的指針reference?front()??{?return?*begin();?}//?獲取最后一個元素reference?back()??{?return?*(end()?-?1);?}//?向容器末尾追加一個元素進來void?push_back(value_type?&&__x)?{?emplace_back(std::move(__x));?}template<typename...?_Args>#if?__cplusplus?>?201402L
????reference#elsevoid#endif
????emplace_back(_Args?&&...?__args);//?將最后一個元素從容器中刪掉void?pop_back()??{
????????--this->_M_impl._M_finish;
????????_Alloc_traits::destroy(this->_M_impl,?this->_M_impl._M_finish);
????}//?在指定位置插入?n?個?__xiterator?insert(const_iterator?__position,?size_type?__n,?const?value_type?&__x)?{
????????difference_type?__offset?=?__position?-?cbegin();
????????_M_fill_insert(begin()?+?__offset,?__n,?__x);return?begin()?+?__offset;
????}//?擦除掉某一個位置上的元素iterator?erase(const_iterator?__position)?{?return?_M_erase(begin()?+?(__position?-?cbegin()));?
????}//?交換兩個容器中的東西void?swap(vector?&__x)??{this->_M_impl._M_swap_data(__x._M_impl);
????????_Alloc_traits::_S_on_swap(_M_get_Tp_allocator(),
??????????????????????????????????__x._M_get_Tp_allocator());
????}//?將容器內所有元素清空void?clear()??{?_M_erase_at_end(this->_M_impl._M_start);?}
};

程序中大量出現的 allocator ,是來自SGI STL的空間分配器,說白了就是用來分配空間內存的。在拿到這部分源碼后,vector 的實現原理也就可以大致有所掌握了(畢竟每個接口也不過幾行?,這可能就是優秀設計的結果??)。而在調用過程中無非就是 vector 調用 vector_base 再調用 allocator 再對 _Vector_impl_data 進行操作。

還有一個問題:在添加元素過程中,如果容器滿了,那么容器的容量是按照怎樣的規則遞增呢?參考了《STL源碼刨析》之后,得到這樣的結論:

如果超過當前容器容量,那么容量會擴增至兩倍,如果兩倍容量仍不夠,那么就擴張至足夠大的容量。

在容量的擴張過程中,必須經歷“重新分配內存,元素移動,釋放原有空間”三個操作,這是因為原有的空間之后不一定能夠滿足需求,所以統一進行這三個操作來完成。

如何知道是擴增兩倍的呢?最直觀直接的方法就是執行一下這個過程看看,例如:

int?main()?{
????vector<int>?v;
????v.push_back(1);
????cout?<"?"?<endl;????//?>:?1?1
????v.push_back(2);
????cout?<"?"?<endl;????//?>:?2?2
????v.push_back(3);
????cout?<"?"?<endl;????//?>:?3?4
????v.push_back(4);
????cout?<"?"?<endl;????//?>:?4?4
????v.push_back(5);
????cout?<"?"?<endl;????//?>:?5?8
}

這樣的容量擴張過程,也帶來另一個問題:當容量為 2 時獲取到的 iterator ,那么在容量為 8 時,還可以用嘛?答案是不一定行,例如:

int?main()?{
????vector<int>?v;
????v.push_back(1);
????v.push_back(2);
????v.push_back(3);
????auto?iter?=?v.begin();
????cout?<endl;??//?>:?1
????v.push_back(4);
????cout?<endl;??//?>:?1
????v.push_back(5);
????cout?<endl;??//?>:?310076128[具體運行結果視內存情況而定]
????iter?=?v.begin();
????cout?<endl;??//?>:?1
}

由此可知,如果當時的內存環境允許,會直接拼到原有容器后面去,如果不允許,那么就需要把當前容器的內容移動到其他地方去了,這時候原來的 iterator 就不能用了。務必小心!

從這一方面也可以體會到,其實 iterator 就是一個類型為傳入 vector 中 T 類型的指針。

在所有的接口中,覺得最有意思的就是 insert 接口了?,它的實現過程比較好玩。

首先,假設調用函數為:insert(position, n, x) ,而且剩余空間夠用,那么它需要分成兩種情況:

  • 插入元素個數 n < 插入點之后的元素個數
  • 插入元素個數 n > 插入點之后的元素個數
  • 上面兩種情況分別對應下面圖中的左右兩邊:

    內存操作示意圖(參考自《STL源碼刨析》)

    在有限的過程和空間里實現最高效的操作,不愧是STL???。

    創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

    總結

    以上是生活随笔為你收集整理的string类的erase函数属于stl吗_探索STL容器:vector的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。