網站首頁 編程語言 正文
動態內存管理
之前我們講述過動態內存的開辟,可以通過new, malloc,以及alloc等方式,本文通過介紹alloc方式,構造一個StrVec類,這個類的功能類似于一個vector,實現字符串的管理,其中包含push一個字符串,動態擴容,析構,回收內存等操作。
StrVec類實現細節
StrVec類實現如下
class StrVec { public: //無參構造函數 StrVec() : elements(nullptr), first_free(nullptr), cap(nullptr) {} //拷貝構造函數 StrVec(const StrVec &); //拷貝賦值運算符 StrVec &operator=(const StrVec &); //析構函數 ~StrVec(); //拷貝元素 void push_back(const std::string &); //返回元素個數 size_t size() const { return first_free - elements; } //返回總容量 size_t capacity() const { return cap - elements; } //返回首元素地址 std::string *begin() const { return elements; } //返回第一個空閑元素地址 //也是最后一個有效元素的下一個位置 std::string *end() const { return first_free; } private: //判斷容量不足開辟新空間 void chk_n_alloc() { if (size() == capacity()) reallocate(); } //重新開辟空間 void reallocate(); // copy指定范圍的元素到新的內存中 std::pair<std::string *, std::string *> alloc_n_copy( const std::string *, const std::string *); //釋放空間 void free(); //數組首元素的指針 std::string *elements; //指向數組第一個空閑元素的指針 std::string *first_free; //指向數組尾后位置的指針 std::string *cap; //構造string類型allocator靜態成員 static std::allocator<std::string> alloc; };
1 elements成員,該成員指向StrVec內部數組空間的第一個元素
2 first_free成員指向第一個空閑元素,也就是有效元素的下一個元素,該元素開辟空間但未構造。
3 cap 指向最后一個元素的下一個位置。
4 alloc為靜態成員,主要負責string類型數組的開辟工作。
5 無參構造函數將三個指針初始化為空,并且默認夠早了alloc。
6 alloc_n_copy私有函數的功能是將一段區域的數據copy到新的空間,
并且返回新開辟的空間地址以及第一個空閑元素的地址(第一個未構造元素的地址)。
7 chk_n_alloc私有函數檢測數組大小是否達到容量,如果達到則調用reallocate重新開辟空間。
8 reallocate重新開辟空間
9 capacity返回總容量
10 size返回元素個數
11 push_back 將元素放入開辟的類似于數組的連續空間中。
12 begin返回首元素地址
13 end返回第一個空閑元素地址,也是最后一個有效元素的下一個位置
無論我們實現push操作還是拷貝構造操作,都要實現realloc,當空間不足時要開辟空間將舊數據移動到新的數據
//重新開辟空間 void StrVec::reallocate() { string *newdata = nullptr; //數組為空的情況 if (elements == nullptr || cap == nullptr || first_free == nullptr) { newdata = alloc.allocate(1); // elements和first_free都指向首元素 elements = newdata; first_free = newdata; // cap指向數組尾元素的下一個位置。 cap = newdata + 1; return; } //不為空則擴充兩倍空間 newdata = alloc.allocate(size() * 2); //新內存空閑位置 auto dest = newdata; //舊內存有效位置 auto src = elements; //通過移動操作將舊數據放到新內存中 for (size_t i = 0; i != size(); ++i) { alloc.construct(dest++, std::move(*src++)); } //移動后舊內存數據無效,一定要刪除 free(); //更新數據位置 elements = newdata; //更新第一個空閑位置 first_free = dest; //更新容量 cap = elements + size() * 2; }
reallocate函數內部判斷是否為剛初始化指針卻沒開辟空間的空數組,如果是則開辟1個大小的空間。
否則則開辟原有空間的兩倍,將舊數據移動到新空間,采用了std::move操作,這么做減少拷貝造成的性能開銷。
move之后原數據就無效了,所以要調用私有函數free()進行釋放。我們實現該free操作
//釋放操作 void StrVec::free() { //判斷elements是否為空 if (elements == nullptr) { return; } auto dest = elements; //要先遍歷析構每一個對象 for (size_t i = 0; i < size(); i++) { // destroy會調用每一個元素的析構函數 alloc.destroy(dest++); } //再整體回收內存 alloc.deallocate(elements, cap - elements); }
先通過遍歷destroy銷毀內存,從而調用string的析構函數,最后在deallocate回收內存。
// copy指定范圍的元素到新的內存中,返回新元素的地址和第一個空閑元素地址的pair std::pair<std::string *, std::string *> StrVec::alloc_n_copy( const std::string *b, const std::string *e) { auto newdata = alloc.allocate(e - b); //將原數據用來初始化新空間 auto first_free = uninitialized_copy(b, e, newdata); return {newdata, first_free}; }
這樣利用alloc_n_copy,我們就可以實現拷貝構造和拷貝賦值了
//拷貝構造函數 StrVec::StrVec(const StrVec &strtmp) { //將形參數據拷貝給自己 auto rsp = alloc_n_copy(strtmp.begin(), strtmp.end()); //更新elements, cap,first_free elements = rsp.first; first_free = rsp.second; cap = rsp.second; }
但是拷貝賦值要注意一點,就是自賦值的情況,所以我們提前判斷是否為自賦值,如不是則進行和拷貝構造相同的操作
//拷貝賦值運算符 StrVec &StrVec::operator=(const StrVec &strtmp) { //防止自賦值 if (this == &strtmp) { return *this; } //將形參數據拷貝給自己 auto rsp = alloc_n_copy(strtmp.begin(), strtmp.end()); //更新elements, cap,first_free elements = rsp.first; first_free = rsp.second; cap = rsp.second; }
我們可以利用free實現析構函數
//析構 StrVec::~StrVec() { free(); }
接下來我們實現push_back,將指定字符串添加到數組空間,以及拋出元素
//添加元素 void StrVec::push_back(const std::string &s) { chk_n_alloc(); alloc.construct(first_free++, s); } //拋出元素 void StrVec::pop_back(std::string &s) { if (first_free == nullptr) { return; } if (size() == 1) { s = *elements; alloc.destroy(elements); first_free = nullptr; elements = nullptr; return; } s = *(--first_free); alloc.destroy(first_free); }
接下來實現測試函數,測試上述操作
void test_strvec() { auto str1 = StrVec(); str1.push_back("hello zack"); StrVec str2(str1); str2.push_back("hello rolin"); StrVec str3 = str1; string strtmp; str3.pop_back(strtmp); }
在主函數調用上面test_strvec,運行穩定。
總結
本文通過allocator實現了一個類似于vector的類,管理string變量。演示了拷貝構造,拷貝賦值要注意的事項,同時演示了如何手動開辟內存并管理內存空間。
原文鏈接:https://blog.csdn.net/secondtonone1/article/details/122856537
相關推薦
- 2022-10-10 Android實現串口通信_Android
- 2022-07-04 Python如何一行輸入多個數,并存入列表_python
- 2022-05-31 jQuery實現側邊導航欄及滑動電梯效果(仿淘寶)_jquery
- 2022-11-09 GO?語言運行環境的基礎知識_Golang
- 2022-04-15 python實現選取或刪除指定列包含指定內容的行_python
- 2022-09-09 Python中Timedelta轉換為Int或Float方式_python
- 2022-01-15 跨域系列之proxy代理,解決跨域的方法之一
- 2022-11-05 一文教你如何使用Databinding寫一個關注功能_Android
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支