STL?vector!!!

一、前言

? ? ? ? 之前我們借助手撕string加深了類和對象相關知識,今天我們將一起手撕一個vector,繼續深化類和對象、動態內存管理、模板的相關知識

二、vector相關的前置知識

? ? ? ? 1、什么是vector?


? ? ? ? vector是一個STL庫中提供的類模板,它是存儲元素對象的順序表,其中提供了一些有關增刪查改的接口,它的特點是可以通過下標的方式在表中的任意位置進行讀、寫

? ? ? ? 2、vector中的相關接口

? ? ? ? 在本文接下來的部分會介紹vector的常用接口,事實上借助這些接口就可以解決平常所能遇到的大部分問題,如果還需要了解vector提供的更多接口及使用方法的話,可以跳轉到一下網頁:
????????vector - C++ Referencehttps://legacy.cplusplus.com/reference/vector/vector/?kw=vector

三、手撕一個vector類

? ? ? ? 1、成員變量與整體框架

? ? ? ? 注意:之前的順序表我們都是通過記錄指針、元素個數和空間大小來完成的,順序表的實現自然也可以這樣,但是STL庫中不是這樣實現的,所以今天我們學習一下STL庫中的實現方式

????????

? ? ? ? 上面的_start、_finish、_endofstorge分別代表著指向有效元素起始位置、終止位置的下一個位置和空間終止位置的迭代器,所以在上面我們已經定義了迭代器,事實上就是指針類型,這是由于順序表的一大特點就是在內存中連續存儲,所以可以直接使用未封裝的指針;另一方面,我們在聲明成員變量的同時都給了一個缺省值,這是由于它們都是指針類型,在初始化時我們都要先初始化成空指針再進行下一步操作,所以我們可以直接在這里給一個缺省值,這樣避免了在初始化列表多次的顯示初始化成空指針

? ? ? ? 2、構造函數

? ? ? ? 庫中的函數頭:
????????

? ? ? ? 以上的三個構造函數我們都會一一實現,在這里補充一點:在上面的構造函數中出現了"clloc",事實上,這是一個內存池,而后面所給的缺省值是STL庫中提供的一個默認的內存池,它可以有效的提高開辟空間的時間消耗,但是比較復雜,所以在這里我們直接new空間,在之后,我們會專門的進行講解

? ? ? ? ? ? ? ? (1).空構造:
????????????????

? ? ? ? ? ? ? ? 在這里實現的空構造是非常簡單的,這是由于我們在聲明時已經給了缺省值,但是該空構造是一定要寫的,這是因為我們還要實現別的構造函數,這時候編譯器就不在自動生成默認構造函數了

? ? ? ? ? ? ? ? (2).用n個元素對象進行初始化:
????????????????

? ? ? ? ? ? ? ? 在使用n個元素對象進行初始化時,我們在參數部分給了一個缺省值T(),很明顯,這是一個匿名對象,同時調用了對應的默認構造函數,這是沒有問題的,但是如果T是int、float等內置類型呢?事實上,這點不用擔心,這是由于C++將這些內置類型都進行了升級,使它們可以象自定義類型一樣調用默認構造函數,舉個例子:如果T是int類型,那么此時T()就會返滬一個0進行初始化

? ? ? ? ? ? ? ? (3).使用一個迭代器區間進行初始化:
????????????????

? ? ? ? ? ? ? ? 在上面的構造函數中,我們再次使用了模板,但這里是函數模板,這是由于我們不僅要支持順序表的迭代器類型初始化,還要支持其它類型的迭代器初始化

? ? ? ? 3、返回順序表一些性質的函數

? ? ? ? ????????(1).size函數:返回此時vector中的元素個數

? ? ? ? ????????庫中的函數頭:
????????????????

? ? ? ? ? ? ? ? 實現:
????????????????

? ? ? ? ? ? ? ? (2).capacity函數:返回此時vector中的空間大小

? ? ? ? ? ? ? ? 庫中的函數頭:
????????????????

? ? ? ? ? ? ? ? 實現:
????????????????

? ? ? ? 4、拷貝構造函數

? ? ? ? 庫中的函數頭:

????????

? ? ? ? 實現:
????????

? ? ? ? 這里需要注意:我們不可以使用memcpy,這是由于memcpy會按字節將x的內容拷貝到_start,但如果T類型是動態開辟內存,也就是說是深拷貝的話,那么雖然vector整體進行了深拷貝,但是vector中的內容卻只完成了淺拷貝,會出現深淺拷貝的問題,此時選擇上面的方式就萬無一失了,這要求元素對象重載了賦值運算符,這一點是必須的

? ? ? ? 5、迭代器相關函數

? ? ? ? ? ? ? ? (1).begin函數:


? ? ? ? ? ? ? ? 庫中的函數頭:
????????????????

? ? ? ? ? ? ? ? begin函數進行了兩個重載,分別是普通begin和const begin它們分別可以返回一個可讀可寫的迭代器和一個只讀不寫的迭代器,都指向vector的首元素位置:
????????????????

? ? ? ? ? ? ? ? (2).end函數:


? ? ? ? ? ? ? ? 庫中的函數頭:
????????????????

? ? ? ? ? ? ? ? end函數仍然進行了兩個重載,返回的迭代器都指向末尾元素的下一個位置:
????????????????

? ? ? ? 6、[]操作符重載

? ? ? ? 庫中的函數頭:
????????

? ? ? ? 庫中對[]操作符進行了兩個重載,與上面的begin類似:
????????

? ? ? ? 庫中對于很多類型進行了封裝,事實上上面我們實現的與庫中的本質是一樣的

? ? ? ? 7、交換函數

? ? ? ? 庫中的函數頭:
????????

? ? ? ? 這里的交換一定是要涉及深拷貝的,在這里我們借用一下算法庫中的swap函數,因為它恰好就是深拷貝:
????????

? ? ? ? 8、賦值運算符重載

? ? ? ? 庫中的函數頭:
????????

? ? ? ? 在這里我們對該函數實現進行一點小改動,但是對于用戶的使用來說是相同的:
????????

? ? ? ? 在上面的實現中,我們采用了傳值傳參,此時形參是實參的拷貝,拷貝會調用我們之前實現過的拷貝構造函數進行深拷貝,再調用swap函數,它也會進行深拷貝之下的交換,最終*this就變成了x的拷貝,最后x這一拷貝出作用域銷毀,調用析構函數(后面會實現),進行了空間的釋放

? ? ? ? 9、開空間相關函數

? ? ? ? ? ? ? ? (1).reserve函數:會開辟指定大小的空間,如果原來有元素,會進行拷貝,否則不進行任何處理

? ? ? ? ? ? ? ? 庫中的函數頭:
? ? ? ? ? ? ? ??

? ? ? ? ? ? ? ? 實現:
????????????????

? ? ? ? ? ? ? ? 在上面我們又遇到了象拷貝構造函數同樣的問題,所以再次使用了賦值運算符重載的方式進行處理

? ? ? ? ? ? ? ? (2).resize函數:會開辟指定大小的空間,用val進行初始化,缺省值為T()

? ? ? ? ? ? ? ? 庫中的函數頭:
????????????????

? ? ? ? ? ? ? ? 實現:
????????????????

? ? ? ? ? ? ? ? 這里我們直接對之前的函數進行復用即可

? ? ? ? 10、插入相關函數

? ? ? ? ? ? ? ? (1).insert函數:在迭代器指定的位置直接插入一個值,返回最后該位置的迭代器

? ? ? ? ? ? ? ? 庫中的函數頭:
????????????????

? ? ? ? ? ? ? ? 實現:
????????????????

? ? ? ? ? ? ? ? 從上面的代碼可以看出:在insert函數內部可能開辟空間,這時候pos就不是原來的pos了,所以在函數外部,傳給過insert函數的迭代器是不能再次使用的,因為使用過的迭代器可能已經失效,這時候我們通過反回值的方式解決了這個問題,所以如果想再次使用的話,要接受函數的返回值

? ? ? ? ? ? ? ? (2).push_back函數:在vector尾部插入一個對象

? ? ? ? ? ? ? ? 庫中的函數頭:
????????????????

? ? ? ? ? ? ? ? 實現:
????????????????

? ? ? ? ? ? ? ? 事實上在這個位置我們直接復用剛才寫過的insert就非常方便

? ? ? ? 11、刪除相關函數

? ? ? ? ? ? ? ? (1).erase函數:刪除迭代器指定位置的元素,返回該位置的迭代器

? ? ? ? ? ? ? ? 庫中的函數頭:
????????????????

? ? ? ? ? ? ? ? 實現:
????????????????

? ? ? ? ? ? ? ? erase所刪除的位置有可能是最后一個,此時刪除之后傳入的迭代器·就失效了,所以要接收返回值并判斷

? ? ? ? ? ? ? ? (2).pop_back函數

? ? ? ? ? ? ? ? 庫中的函數頭:

????????????????

? ? ? ? ? ? ? ? 實現:
????????????????

? ? ? ? ? ? ? ? 直接復用剛才寫過的erase函數即可

? ? ? ? 12、析構函數

? ? ? ? 由于析構函數的特殊性,這里就不提供庫中的函數頭了:

????????

四、vector

????????下面就是我們今天一起完成的vector了:
????????

#include <iostream>
#include <cassert>
#include <algorithm>
using namespace std;
namespace bea
{template<class T>class vector{typedef T* iterator;typedef const T* const_iterator;public://空構造vector() {}//用n個元素對象進行初始化vector(size_t n, const T& val = T()){_start = new T[n + 5];for (size_t i = 0; i < n; i++){_start[i] = val;}_finish = _start + n;_endofstorge = _start + n + 4;}//使用一個迭代器區間進行初始化template<class InputIterator>vector(InputIterator first, InputIterator last){size_t n = last - first;_start = new T[n + 5];InputIterator it1 = first, it2 = _start;while (it1 < last){*it2 = *it1;it1++, it2++;}_finish = _start + n;_endofstorge = _start + n + 4;}//拷貝構造函數vector(const vector& x){size_t sz = x.size();_start = new T[sz + 5];for (size_t i = 0; i < sz; i++){_start[i] = x._start[i];}_finish = _start + sz;_endofstorge = _start + sz + 4;}//size函數size_t size() const{return _finish - _start;}//capacity函數size_t capacity() const{return _endofstorge - _start + 1;}//begin函數iterator begin(){return _start;}const_iterator begin() const{return _start;}//end函數iterator end(){return _finish;}const_iterator end() const{return _finish;}//[]操作符重載T& operator[](size_t pos){assert(pos < size());return _start[pos];}const T& operator[](size_t pos) const{assert(pos < size());return _start[pos];}//交換函數void swap(vector& x){std::swap(x._start, _start);std::swap(x._finish, _finish);std::swap(x._endofstorge, _endofstorge);}//賦值運算符重載vector& operator=(const vector x){swap(x);return *this;}//開空間相關函數void reserve(size_t n){size_t sz = size();if (n <= size()) return;iterator tmp = new T[n + 5];if (_start){for (size_t i = 0; i < sz; i++){tmp[i] = _start[i];}delete[] _start;}_finish = tmp + sz;_start = tmp;_endofstorge = tmp + n + 4;}//insert函數iterator insert(iterator pos, const T& val){assert(pos <= _finish);int n = pos - _start;if (capacity() < size() + 1){reserve(size() + 5);}pos = _start + n;iterator end = _finish - 1, next = _finish;while (next > pos){*next = *end;end--, next--;}*pos = val;_finish++;return pos;}void push_back(const T& val){insert(_finish, val);}void resize(size_t n, T& val = T()){vector(n, val);}//erase函數iterator erase(iterator pos){assert(pos < _finish);iterator end = pos + 1, front = pos;while (end < _finish){*front = *end;front++, end++;}_finish--;if (pos == _finish) return nullptr;else return pos;}//pop_back函數void pop_back(){erase(_finish - 1);}//析構函數~vector(){delete[] _start;_start = nullptr;_finish = nullptr;_endofstorge = nullptr;}private:iterator _start = nullptr;iterator _finish = nullptr;iterator _endofstorge = nullptr;};
}

五、結語

? ? ? ? 這就是我們所實現的vector的全部內容了,我們的目的仍然是了解vector的用法、加深類和對象和模板等知識點的理解,感謝大家的閱讀,歡迎各位于晏、亦菲和我一起交流、學習、進步!

????????????????

? ? ? ? ? ? ? ?

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

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

相關文章

C++學習之路,從0到精通的征途:繼承

目錄 一.繼承的概念及定義 1.繼承的概念 2.繼承的定義 (1)繼承的定義格式 (2)繼承基類成員訪問方式的變化 二.基類與派生類間的轉換 1.派生類對象賦值給基類的引用/指針 2. 派生類對象直接賦值給基類對象 三.繼承的作用域 四.派生類的默認成員函數 1.構造函數 2.拷…

用vue和go實現登錄加密

前端使用CryptoJS默認加密方法&#xff1a; var pass CryptoJS.AES.encrypt(formData.password, key.value).toString()使用 CryptoJS.AES.encrypt() 時不指定加密模式和參數時&#xff0c;CryptoJS 默認會執行以下操作 var encrypted CryptoJS.AES.encrypt("明文&quo…

React百日學習計劃——Deepseek版

階段一&#xff1a;基礎鞏固&#xff08;1-20天&#xff09; 目標&#xff1a;掌握HTML/CSS/JavaScript核心語法和開發環境搭建。 每日學習內容&#xff1a; HTML/CSS&#xff08;1-10天&#xff09; 標簽語義化、盒模型、Flex布局、Grid布局、響應式設計&#xff08;媒體查詢…

WPF中如何自定義控件

WPF自定義控件簡化版&#xff1a;賬戶菜單按鈕&#xff08;AccountButton&#xff09; 我們以**“賬戶菜單按鈕”為例&#xff0c;用更清晰的架構實現一個支持標題顯示、漸變背景、選中狀態高亮**的自定義控件。以下是分步拆解&#xff1a; 一、控件核心功能 我們要做一個類似…

Deepseek+Xmind:秒速生成思維導圖與流程圖

deepseekxmind&#xff0c;快速生成思維導圖和流程圖 文章目錄 思維導圖deepseek筆記本 txt文件xmind 流程圖deepseekdraw.io 思維導圖 deepseek 筆記本 txt文件 將deep seek的東西復制到文本文件中&#xff0c;然后將txt文件拓展名改成md xmind 新建思維導圖----左上角三…

基于javaweb的SpringBoot愛游旅行平臺設計和實現(源碼+文檔+部署講解)

技術范圍&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬蟲、數據可視化、小程序、安卓app、大數據、物聯網、機器學習等設計與開發。 主要內容&#xff1a;免費功能設計、開題報告、任務書、中期檢查PPT、系統功能實現、代碼編寫、論文編寫和輔導、論文…

服務器機架的功能和重要性

服務器已經成為各個行業必不可少的網絡設備&#xff0c;而服務器機架則是數據中心和IT基礎設施中不可或缺的重要組成部分&#xff0c;服務器機架能夠為服務器和其他網絡設備提供物理支撐&#xff0c;同時還可以提供設備維護和管理等多種功能&#xff0c;本文就來介紹一下服務器…

游戲引擎學習第277天:稀疏實體系統

回顧并為今天定下基調 上次我們結束的時候&#xff0c;基本上已經控制住了跳躍的部分&#xff0c;達到了我想要的效果&#xff0c;現在我們主要是在等待一些新的藝術資源。因此&#xff0c;等新藝術資源到位后&#xff0c;我們可能會重新處理跳躍的部分&#xff0c;因為現在的…

阿克曼-幻宇機器人系列教程1- 實現上位機與下位機交互的兩種方式

1. 電腦與機器人通過SSH命令連接 1.1 將機器人上電 目的&#xff1a;將機器人變成熱點 目標&#xff1a;將電腦連接機器人網絡 熱點名稱&#xff1a;Huanyu-111 密碼&#xff1a;12345678 1.2 完成電腦與機器人之間的連接 實現&#xff1a;在電腦終端中執行命令通過SSH登錄…

Rust 中的 Pin 和 Unpin:內存安全與異步編程的守護者

在 Rust 的世界里&#xff0c;Pin 和 Unpin 是兩個看似不起眼、實則至關重要的概念。它們在內存安全和異步編程中扮演著關鍵角色&#xff0c;是 Rust 開發者必須掌握的知識。今天&#xff0c;就讓我們深入探討這兩個概念&#xff0c;看看它們是如何在 Rust 的生態系統中發揮作用…

如何界定合法收集數據?

首席數據官高鵬律師團隊 在當今數字化時代&#xff0c;數據的價值日益凸顯&#xff0c;而合法收集數據成為了企業、機構以及各類組織必須嚴守的關鍵準則。作為律師&#xff0c;深入理解并準確界定合法收集數據的范疇&#xff0c;對于保障各方權益、維護法律秩序至關重要。 一…

自動駕駛的“眼睛”:用Python構建智能障礙物檢測系統

自動駕駛的“眼睛”:用Python構建智能障礙物檢測系統 在自動駕駛技術日益成熟的今天,障礙物檢測系統成了汽車智能化不可或缺的部分。無論是高速公路上的突發狀況,還是城市街道中的行人與車輛,準確識別障礙物并及時反應,是保證行車安全的關鍵。 那么,我們如何用Python構…

19.Excel數據透視表:第2部分數據透視計算

一 日期組合 不想看具體是哪一天的收入&#xff0c;想看每個月的收入是多少&#xff0c;要對日期進行組合。 光標選中日期字段下的數據&#xff0c; 右鍵。 補充&#xff1a;第2種方法。 補充&#xff1a;可以同時選擇多個。 下面這個是錯誤的。 源數據里面有不同的年份&#x…

Eclipse 插件開發 6 右鍵菜單

Eclipse 插件開發 6 右鍵菜單 1 plugin.xml2 SampleHandler.java3 Activator.java 1 plugin.xml <?xml version"1.0" encoding"UTF-8"?> <?eclipse version"3.4"?> <plugin><!-- 定義命令 --><extension point&…

用vite腳手架建立 前端工程

? 參考 開始 | Vite 官方中文文檔 腳本 chcp 65001 echo 建立vite工程 set PRO_NAMEmy-vue-app call npm create vitelatest %PRO_NAME% --template vue cd ./%PRO_NAME%set NOW_PATH%cd% echo now_path %NOW_PATH% echo 點擊回車啟動vite工程&#xff0c;請訪問ht…

ESP32C3連接wifi

文章目錄 &#x1f527; 一、ESP32-C3 連接 Wi-Fi 的基本原理&#xff08;STA 模式&#xff09;? 二、完整代碼 注釋講解&#xff08;適配 ESP32-C3&#xff09;&#x1f4cc; 三、幾個關鍵點解釋&#x1f51a; 四、小結 &#x1f527; 一、ESP32-C3 連接 Wi-Fi 的基本原理&a…

LangSmith 基本使用教程

LangSmith 是一個強大的工具&#xff0c;可以幫助開發者追蹤、監控和分析語言模型應用程序的性能。下面我將介紹兩種基本的追蹤方式&#xff1a;追蹤 OpenAI 調用和追蹤整個應用程序。 1. 追蹤 OpenAI 調用 (Trace OpenAI calls) 這種方法主要用于追蹤對 OpenAI API 的調用&a…

Python基礎學習-Day23

目錄 基礎概念轉換器&#xff08;transformer&#xff09;估計器&#xff08;estimator&#xff09;管道&#xff08;pipeline&#xff09; 實例pipeline 基礎概念 pipeline在機器學習領域可以翻譯為“管道”&#xff0c;也可以翻譯為“流水線”&#xff0c;是機器學習中一個重…

相對論速度疊加公式與雙曲正切

復習下相對論速度疊加公式吧&#xff0c;物理&#xff0c;是不是很多人都忘了呀。假設速度為 u , v u,v u,v&#xff0c;那么疊加后的速度 w w w為&#xff1a; w u v 1 u v / c 2 w\frac{uv}{1uv/c^2} w1uv/c2uv? ??這個公式告訴我們&#xff0c;在一個速度為2/3光速的…

【前綴和】和為 K 的子數組(medium)

【前綴和】和為 K 的子數組 題目描述算法原理和細節問題代碼 題目描述 和為 K 的子數組 給定一個整數數組和一個整數 k &#xff0c;請找到該數組中和為 k 的連續子數組的個數。 示例 1&#xff1a; 輸入:nums [1,1,1], k 2 輸出: 2 解釋: 此題 [1,1] 與 [1,1] 為兩種不同的…