【C++初階】--- vector容器功能模擬實現

1.什么是vector?

在 C++ 里,std::vector 是標準模板庫(STL)提供的一個非常實用的容器類,它可以看作是動態數組
在這里插入圖片描述

2.成員變量

iterator _start;:指向 vector 中第一個元素的指針。
iterator _finish;:指向 vector 中最后一個元素的下一個位置的指針。
iterator _end_of_storage;:指向 vector 所分配內存空間的末尾的指針。

template<class T>
class vector
{
public:typedef T* iterator;typedef const T* const_iterator;
private:iterator _start = nullptr;iterator _finish = nullptr;iterator _end_of_storage = nullptr;
}

3.構造函數

3.1默認構造

因為三個成員變量都有缺省值,我們只需要顯示寫默認構造即可,他們會在初始化鏈表處初始化。

//寫法1:
vector()
{}
//寫法2:
vector() = default;//C++11支持強制生成默認構造

3.2拷貝構造

  1. 調用 reserve 函數,提前為新 vector 對象分配足夠的內存空間,其大小和 v 的元素數量相同,此步驟可以避免后續添加元素時的頻繁擴容。
  2. 借助范圍 for 循環遍歷 v 中的每個元素,使用引用 auto& 避免不必要的拷貝(特別是當元素為自定義類型時)
  3. 對 v 中的每個元素,調用 push_back 函數將其添加到新 vector 對象的末尾。
//拷貝構造
vector(const vector<T>& v)
{//提前開好空間reserve(v.size());//如果元素是自定義類型,用引用可以減少拷貝for (auto& ch : v){//尾插數據push_back(ch);}
}

3.3迭代器構造函數

迭代器構造函數需要我們傳兩個迭代器,我們使用范圍for將元素尾插至新構造的vector對象中,構造的范圍是[first,last)
注意:類模板的成員函數,也可以是函數模板,我們可以將這個迭代器構造函數寫成模板,優點是可以用不同的類構造vector對象,前提是元素類型需相同,如:int

//迭代器構造
template<class InputIterator>
vector(InputIterator first, InputIterator last)
{while (first != last){push_back(*first);first++;}
}

3.4半缺省構造函數

我們可以通過半缺省構造函數構造一個有n個val值得vector對象

  1. 使用resver提前開好空間,避免后續插入元素時頻繁擴容
  2. 使用for循環尾插n個值為val的元素,如果不傳參使用的是缺省值T()
  3. 對于內置類型(如 int、double、char 等),T() 會進行值初始化。對于數值類型,會初始化為 0;對于指針類型,會初始化為 nullptr。
  4. 對于自定義類型,T() 會調用該類型的默認構造函數。如果沒有顯式定義默認構造函數,編譯器會自動生成一個(前提是該類沒有其他帶參數的構造函數

注意:我們使用半缺省構造函數的時候傳參需要注意,如下:如果不顯示第一個參數是size_t,編譯器會優先調用更符合的構造函數,即會上面的迭代器構造函數

//u表示10是size_t類型
vector<int> v1(10u,1);
//用n個val進行構造,T()是匿名對象
vector(size_t n, const T& val = T())
{reserve(n);for (size_t i = 0; i < n; i++){//如果用這種,_finish記得++//*_finish = val;//_finish++;push_back(val);}
}

4.析構函數

三個指針指向同一塊空間的不同位置,使用delete釋放的時候我們需要使用指向vector中第一個元素的指針,同一塊空間不能進行多次釋放。

//析構
~vector()
{if (_start){delete[] _start;_start = _finish = _end_of_storage = nullptr;}
}

5.iterators

5.1begin()/end()

//普通對象使用
iterator begin()
{return _start;
}iterator end()
{return _finish;
}//const對象使用
const_iterator begin() const
{return _start;
}const_iterator end() const
{return _finish;
}

6.內聯函數

6.1size()

size_t size() const
{return _finish - _start;
}

6.2capacity()

size_t capacity() const
{return _end_of_storage - _start;
}

6.3empty()

bool empty() const
{return _start == _finish;
}

6.4clear()

void clear()
{_finish = _start;
}

6.5swap()

void swap(vector<T>& v)
{std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_end_of_storage, v._end_of_storage);
}

6.7=運算符重載

普通寫法:

  1. 先清除當前vector對象中的所有元素
  2. 提前開頭看見,避免后續頻繁擴容
  3. 使用范圍for依次將元素尾插至當前vector對象
//賦值運算符重載
vector<T>& operator=(const vector<T>& v)
{if (this != &v){//先清理原先數據clear();//提前開好空間reserve(v.size());//如果數據是自定義類型可以減少拷貝for (auto& ch : v){push_back(ch);}}return *this;
}

現代寫法:

//賦值運算符重載,現代寫法
//這里是拷貝,不是引用,且不加const
vector<T>& operator=(vector<T> v)
{swap(v);return *this;
}

6.8[]運算符重載

//普通對象使用
T& operator[](size_t i)
{//i不能越界assert(i < size());return _start[i];
}//const對象使用
const T& operator[](size_t i) const
{//i不能越界assert(i < size());return _start[i];
}

7.尾插/尾刪元素

7.1push_back()

  1. 判斷空間是否已滿,已滿需進行擴容操作,使用三木操作符,如果當前vector對象空間對空,擴容至4個元素,不為空則選擇2倍擴容。
  2. 尾插元素,_finish指針向后移動一位
//尾插
//如果是自定義類型,用引用可以減少拷貝
void push_back(const T& x)
{//擴容if (_finish == _end_of_storage){reserve(capacity() == 0 ? 4 : 2 * capacity());}*_finish = x;_finish++;
}

7.2pop_back()

  1. 判斷當前vector對象中是否還剩余元素,元素為0則斷言失敗
  2. 還有元素則將_finish指針前移一位
//尾刪
void pop_back()
{//判空assert(!empty());_finish--;
}

8.在pos位置插入刪除元素

8.1insert()

  1. 判斷pos位置的有效性,可插入范圍為[_start,_finish]
  2. 判斷空間是否足夠,不夠則進行擴容操作
    注意:如果進行擴容操作,則會造成迭代器失效,需要更新下迭代器
  3. 將pos位置及之后的數據向后移動一位
  4. 在pos位置插入元素,返回pos位置的迭代器
//在pos位置插入數據
iterator insert(iterator pos, const T& x)
{//pos有效性assert(pos >= _start && pos <= _finish);//擴容if (_finish == _end_of_storage){size_t len = pos - _start;reserve(capacity() == 0 ? 4 : 2 * capacity());//擴容后pos迭代器會失效,需要更新一下pos = _start + len;}//將pos及之后的數據往后移動一位iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;--end;}*pos = x;++_finish;return pos;
}

8.2erase()

  1. 判斷pos位置的有效性,可刪除范圍為[_start,_finish)
  2. 將pos之后的數據向前移動一位,將pos位置的數據進行覆蓋
  3. 別忘了將_finish指針也前移一位
//刪除pos位置的數據
template<class T>
void vector<T>::erase(iterator pos)
{//pos有效性assert(pos >= _start && pos < _finish);//向前移動數據iterator it = pos + 1;while (it != end()){*(it - 1) = *it;it++;}--_finish;
}

9.空間大小和數據大小調整

9.1resize()

將vector對象中的數據個數調整至n個,如果n小于當前元素個數,將當前元素個數調整為n,如果n大于當前元素個數,使用val對其進行填充。

//調整長度
template<class T>
void vector<T>::resize(size_t n, T val)
{//n小于長度,縮減長度if (n < size()){_finish = _start + n;}//n大于長度,在后面補元素else{//可能需要擴容reserve(n);//從原數據結尾開始補while (_finish < _start + n){//*_finish = val;//_finish++;push_back(val);}}
}

9.2reserve()

將空間大小擴容至n,如果n小于當前空間大小,不進行操作和改變

  1. 保存舊空間大小old_size,否則后續:
    _start = tmp;
    _finish = tmp + size();
    而size()是_finish-_start,從而_finish = tmp + size()=_start+_finish-_start=_finish
  2. new新空間,大小為n
  3. 將原對象內容進行深拷貝,不能使用memcpy進行拷貝
    在這里插入圖片描述
  4. 釋放原空間,更新成員變量
template<class T>
void vector<T>::reserve(size_t n)
{//n大于空間大小才擴容if (n > capacity()){size_t old_size = size();//new個新空間T* tmp = new T[n];//memcpy對于內置類型是深拷貝,對于自定義類型是淺拷貝,所以不能用memcpy//memcpy(tmp, _start, size() * sizeof(T));for (size_t i = 0; i < size(); i++){tmp[i] = _start[i];}//銷毀舊空間數據delete[] _start;_start = tmp;_finish = tmp + old_size;_end_of_storage = tmp + n;}
}

10打印vector對象內容

C++規定:沒有實例化的類模板里取東西,編譯器不能區分這里的const_vector是類型還是變量,在前面加上typename表示取的是類型。
當然也可以使用auto自動識別類型。

//打印順序表的模板
template<class T>
void print_vector(const vector<T>& v)
{//auto it = v.begin();typename vector<T>::const_iterator it = v.begin();while (it != v.end()){cout << *it << " ";it++;}cout << endl;/*	for (auto ch : v){cout << ch << " ";}cout << endl;*/
}

通用打印模板:

//通用打印模板
template<class Container>
void print_vector(const Container& v)
{auto it = v.begin();while (it != v.end()){cout << *it << " ";it++;}cout << endl;//或者//for (auto ch : v)//{//	cout << ch << " ";//}//cout << endl;
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/76538.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/76538.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/76538.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

分布式鎖在秒殺場景中的Python實現與CAP權衡

目錄 一、分布式鎖的前世今生 二、秒殺系統的 “硬核” 挑戰 三、Python 實現分布式鎖的 “實戰演練” Redis 實現:快準狠 ZooKeeper 實現:穩如老狗 數據庫實現:老實本分 四、CAP 理論的 “三角戀” 五、性能優化的 “錦囊妙計” 鎖粒度控制:粗細有道 超時機制:別…

企業級開發SpringBoost玩轉Elasticsearch

案例 Spring Boot 提供了 spring-data-elasticsearch 模塊&#xff0c;可以方便地集成 Elasticsearch。 下面我們將詳細講解如何在 Spring Boot 中使用 Elasticsearch 8&#xff0c;并提供示例代碼。 1. 添加依賴: 首先&#xff0c;需要在 pom.xml 文件中添加 spring-data-e…

磐石云智能語音客服系統——技術革新引領服務新體驗

在人工智能技術飛速發展的今天&#xff0c;企業對于智能化客戶服務的需求日益增長。磐石云智能語音客服系統憑借其前沿技術架構與深度場景適配能力&#xff0c;正在重新定義人機交互的邊界。本文將深入解析該系統如何通過技術創新實現服務效率與體驗的雙重突破。 一、意圖識別…

OpenGL學習筆記(assimp封裝、深度測試、模板測試)

目錄 模型加載Assimp網格模型及導入 深度測試深度值精度深度緩沖的可視化深度沖突 模板測試物體輪廓 GitHub主頁&#xff1a;https://github.com/sdpyy1 OpenGL學習倉庫:https://github.com/sdpyy1/CppLearn/tree/main/OpenGLtree/main/OpenGL):https://github.com/sdpyy1/CppL…

通過AWS EKS 生成并部署容器化應用

今天給大家分享一個實戰例子&#xff0c;如何在EKS上創建容器化應用并通過ALB來發布。先介紹一下幾個基本概念&#xff1a; IAM, OpenID Connect (OIDC) 2014 年&#xff0c;AWS Identity and Access Management 增加了使用 OpenID Connect (OIDC) 的聯合身份支持。此功能允許…

入侵檢測snort功能概述

1. 數據包嗅探與日志記錄 網絡流量監控&#xff1a;實時捕獲和分析網絡數據包&#xff08;支持以太網、無線等&#xff09;。 日志記錄&#xff1a;將數據包以二進制格式&#xff08;pcap&#xff09;或文本格式存儲&#xff0c;供后續分析。 2. 協議分析與解碼 深度協議解析…

【Easylive】定時任務-每日數據統計和臨時文件清理

【Easylive】項目常見問題解答&#xff08;自用&持續更新中…&#xff09; 匯總版 這個定時任務系統主要包含兩個核心功能&#xff1a;每日數據統計和臨時文件清理。下面我將詳細解析這兩個定時任務的實現邏輯和技術要點&#xff1a; Component Slf4j public class SysTas…

藍橋杯 15g

班級活動 問題描述 小明的老師準備組織一次班級活動。班上一共有 nn 名 (nn 為偶數) 同學&#xff0c;老師想把所有的同學進行分組&#xff0c;每兩名同學一組。為了公平&#xff0c;老師給每名同學隨機分配了一個 nn 以內的正整數作為 idid&#xff0c;第 ii 名同學的 idid 為…

如何使用AI輔助開發R語言

R語言是一種用于統計計算和圖形生成的編程語言和軟件環境&#xff0c;很多學術研究和數據分析的科學家和統計學家更青睞于它。但對與沒有編程基礎的初學者而言&#xff0c;R語言也是有一定使用難度的。不過現在有了通義靈碼輔助編寫R語言代碼&#xff0c;我們完全可以用自然語言…

CISCO組建RIP V2路由網絡

1.實驗準備&#xff1a; 2.具體配置&#xff1a; 2.1根據分配好的IP地址配置靜態IP&#xff1a; 2.1.1PC配置&#xff1a; PC0&#xff1a; PC1&#xff1a; PC2&#xff1a; 2.1.2路由器配置&#xff1a; R0&#xff1a; Router>en Router#conf t Enter configuration…

React + TipTap 富文本編輯器 實現消息列表展示,類似Slack,Deepseek等對話框功能

經過幾天折騰再折騰&#xff0c;弄出來了&#xff0c;弄出來了&#xff01;&#xff01;&#xff01; 消息展示 在位編輯功能。 兩個tiptap實例1個用來展示 消息列表&#xff0c;一個用來在位編輯消息。 tiptap靈活富文本編輯器&#xff0c;拓展性太好了!!! !!! 關鍵點&#x…

Ubuntu搭建Pytorch環境

Ubuntu搭建Pytorch環境 例如&#xff1a;第一章 Python 機器學習入門之pandas的使用 提示&#xff1a;寫完文章后&#xff0c;目錄可以自動生成&#xff0c;如何生成可參考右邊的幫助文檔 文章目錄 Ubuntu搭建Pytorch環境前言一、Anaconda二、Cuda1.安裝流程2、環境變量&#…

Sping Cloud配置和注冊中心

1.Nacos實現原理了解嗎&#xff1f; Nacos是注冊中心&#xff0c;主要是幫助我們管理服務列表。Nacos的實現原理大概可以從下面三個方面來講&#xff1a; 服務注冊與發現&#xff1a;當一個服務實例啟動時&#xff0c;它會向Nacos Server發送注冊請求&#xff0c;將自己的信息…

C++筆記之父類引用是否可以訪問到子類特有的屬性?

C++筆記之父類引用是否可以訪問到子類特有的屬性? code review! 參考筆記 1.C++筆記之在基類和派生類之間進行類型轉換的所有方法 文章目錄 C++筆記之父類引用是否可以訪問到子類特有的屬性?1.主要原因2.示例代碼3.說明4.如何訪問子類特有的屬性5.注意事項6.總結在 C++ 中,…

JavaScript逆向工程:如何判斷對稱加密與非對稱加密

在現代Web應用安全分析中&#xff0c;加密算法的識別是JavaScript逆向工程的關鍵環節。本文將詳細介紹如何在逆向工程中判斷JavaScript代碼使用的是對稱加密還是非對稱加密。 一、加密算法基礎概念 1. 對稱加密 (Symmetric Encryption) 特點&#xff1a;加密和解密使用相同的…

物理備份工具 BRM vs gs_probackup

什么是BRM 上一篇文章講了openGauss的物理備份工具gs_probackup&#xff0c;今天來說說BRM備份工具。 BRM備份恢復工具全稱為&#xff1a;Backup and Recovery Manager&#xff0c;是MogDB基于opengauss的備份工具 gs_probackup 做了一些封裝和優化,面向MogDB數據庫實現備份和…

問問lua怎么寫DeepSeek,,,,,

很坦白說&#xff0c;這十年&#xff0c;我幾乎沒辦法從互聯網找到這個這樣的代碼&#xff0c;互聯網引擎找不到&#xff0c;我也沒有很大的“追求”要傳承&#xff0c;或者要宣傳什么&#xff1b;直到DeepSeek的出現 兄弟&#xff0c;Deepseek現在已經比你更了解你樓下的超市…

react+Tesseract.js實現前端拍照獲取/選擇文件等文字識別OCR

需求背景 在開發過程中可能會存在用戶上傳一張圖片后下方需要自己識別出來文字數字等信息&#xff0c;有的時候會通過后端來識別后返回&#xff0c;但是也會存在純前端去識別的情況&#xff0c;這個時候就需要使用到Tesseract.js這個庫了 附Tesseract.js官方&#xff08;htt…

藍橋杯考前復盤

明天就是考試了&#xff0c;適當的停下刷題的步伐。 靜靜回望、思考、總結一下&#xff0c;我走過的步伐。 考試不是結束&#xff0c;他只是檢測這一段時間學習成果的工具。 該繼續走的路&#xff0c;還是要繼續走的。 只是最近&#xff0c;我偶爾會感到迷惘&#xff0c;看…

前端-Vue3

1. Vue3簡介 2020年9月18日&#xff0c;Vue.js發布版3.0版本&#xff0c;代號&#xff1a;One Piece&#xff08;n 經歷了&#xff1a;4800次提交、40個RFC、600次PR、300貢獻者 官方發版地址&#xff1a;Release v3.0.0 One Piece vuejs/core 截止2023年10月&#xff0c;最…