C++基礎:模擬實現vector(有存在深層次的淺拷貝問題)

目錄

引言?

一、vector的基本框架

二、尾插push_back、reserve擴容、任意位置插入insert(增)

1.reserve擴容

2.push_back尾插

3.深層次的淺拷貝問題

4.?任意位置插入數據insert(會使迭代器失效)

三、構造、析構、拷貝構造函數

1.構造函數

1.1無參構造

1.2initializer_list?類作為參數來構造。

1.3用任意類型的迭代器來構造

2.拷貝構造

3.析構函數

四、判空函數(empty)、尾刪(pop_back)、刪除任意位置元素(erase)

1.判空

2.尾刪

3.刪除任意位置元素(會使迭代器失效)

五、[]運算符重載、=賦值運算符重載、交換vector函數

1.swap函數

2.=賦值運算符重載

3.[]運算符重載

六、vector.h代碼


引言?

手把手帶你實現vector

實現vector的意義:對底層有個更深的理解。鍛煉代碼能力。

_涂色_-博客主頁

C++基礎專欄

為了與STL中的vector區分開,這里都寫在命名空間stl中,并且實現類函數的聲明和定義分離。?

分3個文件來寫:

  • string.h:string類的聲明。
  • text.c?:測試string的接口是否正確。

因為:模板聲明和定義不能分離定義到兩個文件。所以都在h .cpp文件中。

一、vector的基本框架

這里是為了保持和STL源碼的私有成員一樣所以這樣設計。

這里的迭代器只是以簡單的形式來實現的,真實的迭代器不是這樣的。

namespace stl
{template<class T>class vector{public:typedef T* iterator;typedef const T* const_iterator;const_iterator begin() const //vector開始位置{return _start;}const_iterator end() const  //vector結束位置{return _finish;}iterator begin(){return _start;}iterator end(){return _finish;}int capacity() const  //vector里面的個數{return _endofstorage - _start;}int size() const  //計算出vector數組中元素個數{return _finish - _start;}private:iterator _start = nullptr;  // 存儲數據的數組iterator _finish = nullptr;  //數組結尾的寫一個位置iterator _endofstorage = nullptr;  //數組的空間的最后一個位置};
}

二、尾插push_back、reserve擴容、任意位置插入insert(增)

1.reserve擴容

先實現reserve擴容

注意:必須先計算出原來的元素個數,若在下面計算元素個數的時候,由于_start已經發生了改變,會使得計算元素個數錯誤。

? ? ? ? 關于深層次的淺拷貝問題,寫出push_back來給出說明。

void reserve(const size_t n)
{if (n > capacity()){size_t old_size = size();T* tmp = new T[n];//拷貝舊空間數據到新空間數據if (_start){//memcpy(tmp, _start, sizeof(T*) * old_size); 會存在深層次的淺拷貝問題。for (size_t i = 0; i < old_size; i++){tmp[i] = _start[i];}delete[] _start;}_start = tmp;_finish = _start + old_size;_endofstorage = _start + n;}
}

2.push_back尾插

先看空間夠不夠,不夠就先擴容,然后尾插即可。

void push_back(const T& x)
{if (_finish == _endofstorage){reserve(capacity() == 0 ? 4 : capacity() * 2);}*_finish = x;++_finish;
}

3.深層次的淺拷貝問題

運行下面代碼,當尾插5個string類型的數據時,由于需要擴容,就需要拷貝原理的數據。

namespace stl
{	template<class container>void Print(const container& con){for (auto e : con){cout << e << " ";}cout << endl;}void test_vector1(){vector<string> v1;v1.push_back("11111111111111111111111");v1.push_back("11111111111111111111111");v1.push_back("11111111111111111111111");v1.push_back("11111111111111111111111");v1.push_back("11111111111111111111111");Print(v1);}
}int main()
{stl::test_vector1();return 0;
}

但是,如果使用memcpy拷貝數據的話,就會形成生層次的淺拷貝問題。

當使用memcpy拷貝時:

?所以這里不能使用memcpy來進行拷貝,要通過賦值操作符,調用函數的拷貝構造,來進行拷貝。

4.?任意位置插入數據insert(會使迭代器失效)

在 C++ 里,迭代器失效是一個重要概念,它指的是由于容器結構發生改變,導致原本指向容器中某個元素的迭代器不再有效。此時若繼續使用這個迭代器,可能會引發程序崩潰、得到錯誤結果等嚴重問題。

  1. 先看空間夠不夠,不夠的話就先擴容,擴容的時候,需要更新pos指針,否則pos位置就被釋放了(pos就是野指針了)。
  2. 將pos位置和后面的元素向后移動一位
  3. 最后在pos位置插入元素x
iterator insert(iterator pos, const T& x) //給返回值是解決迭代器失效
{assert(pos >= _start);assert(pos <= _finish);if (_finish == _endofstorage){size_t len = pos - _start;reserve(capacity() == 0 ? 4 : capacity() * 2);// 擴容后這里需要更新pos,否則pos就是野指針了pos = _start + len;}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;--end;}*pos = x;++_finish;return pos;
}

三、構造、析構、拷貝構造函數

1.構造函數

1.1無參構造

可以手動寫,但是沒有必要,因為成員給了缺省值,所以,不帶慘的構造使用編譯器生成的即可。

	vector():_start = nullptr,_finish = nullptr, _endofstorage = nullptr{}

但是下面會寫拷貝構造,所以編譯器不會自己生成構造函數了,所以:

		// 強制編譯器生成默認構造vector() = default;

1.2initializer_list<T>?類作為參數來構造。

使用?initializer_list<T>?類作為參數來構造。

vector(initializer_list<T> il)
{reserve(il.size());for (auto& e : il){push_back(e);}
}

通過這種構造,可以這樣初始化vector:

vector<int> v1 = { 1,2,3,4,5,5 };
vector<int> v2 = { 1,2,3,4,5,51,1,1,1,1,1,1,1 };

原因就是通過?initializer_list<T>類來進行初始化。

1.3用任意類型的迭代器來構造

當我們想用string的一段,來初始化vector時:

		// 函數模板// 任意類型容器迭代器初始化template <class InputIterator>vector(InputIterator first, InputIterator last){while (first != last){push_back(*first);++first;}}

運行下面這段代碼?

string s1("hello world");
vector<int> v6(s1.begin(), s1.end());
Print(v6);

?

2.拷貝構造

直接開一樣的空間,進行尾插來拷貝構造

		//v2(v)vector(const vector<T>& v){reserve(v.capacity());for (auto& e : v){push_back(e);}}

3.析構函數

?釋放數組,然后都置為空。

		~vector<T>(){if (_start){delete[] _start;_start = _finish = _endofstorage = nullptr;}}

四、判空函數(empty)、尾刪(pop_back)、刪除任意位置元素(erase)

1.判空

  • 是空,就返回真,
  • 不是空,就返回假
bool empty() const
{return _start == _finish;
}

2.尾刪

有了判空函數,就不需要使用assert來斷言了。

void pop_back()
{//assert(_finish > _start);assert(!empty());--_finish;}

3.刪除任意位置元素(會使迭代器失效)

iterator erase(const iterator pos)  //給返回值是解決迭代器失效
{assert(pos >= _start);assert(pos < _finish);iterator it = pos + 1;while (it != _finish){*(it - 1) = *it;++it;}return pos;
}

五、[]運算符重載、=賦值運算符重載、交換vector函數

1.swap函數

交換兩個vector

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

2.=賦值運算符重載

tmp是v2的拷貝,然后和v1直接交換,就使得v1 等于 v2了。

// V1 = V2
vector<int>& operator=(vector<T> tmp)
{swap(tmp);return *this;
}

3.[]運算符重載

可以通過下標來訪問vector了

T& operator[](size_t i)
{assert(i < size());return _start[i];
}const T& operator[](size_t i) const
{assert(i < size());return _start[i];
}

六、vector.h代碼

#pragma once
#include<iostream>
#include<assert.h>
#include<vector>
using namespace std;
// 模板 聲明和定義不能分離定義到兩個文件.h .cppnamespace stl
{template<class T>class vector{public:typedef T* iterator;typedef const T* const_iterator;const_iterator begin() const{return _start;}const_iterator end() const{return _finish;}iterator begin(){return _start;}iterator end(){return _finish;}// 強制編譯器生成默認構造vector() = default;vector(initializer_list<T> il){reserve(il.size());for (auto& e : il){push_back(e);}}//v2(v)vector(const vector<T>& v){reserve(v.capacity());for (auto& e : v){push_back(e);}}~vector<T>(){if (_start){delete[] _start;_start = _finish = _endofstorage = nullptr;}}// V1 = V2vector<int>& operator=(vector<T> tmp){swap(tmp);return *this;}// 函數模板// 任意類型容器迭代器初始化template <class InputIterator>vector(InputIterator first, InputIterator last){while (first != last){push_back(*first);++first;}}/*vector(iterator first, iterator last){while (first != last){push_back(*first);++first;}}*/vector(int n, int val = T()){resize(n, val);}vector(size_t n, T val = T()){resize(n, val);}void swap(vector<T> v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_endofstorage, v._endofstorage);}T& operator[](size_t i){assert(i < size());return _start[i];}const T& operator[](size_t i) const{assert(i < size());return _start[i];}void reserve(const size_t n){if (n > capacity()){size_t old_size = size();T* tmp = new T[n];//拷貝舊空間數據到新空間數據if (_start){//memcpy(tmp, _start, sizeof(T*) * old_size); 會存在深層次的淺拷貝問題。for (size_t i = 0; i < old_size; i++){tmp[i] = _start[i];}delete[] _start;}_start = tmp;_finish = _start + old_size;_endofstorage = _start + n;}}int capacity() const{return _endofstorage - _start;}int size() const{return _finish - _start;}void push_back(const T& x){if (_finish == _endofstorage){reserve(capacity() == 0 ? 4 : capacity() * 2);}*_finish = x;++_finish;}void pop_back(){//assert(_finish > _start);assert(!empty());--_finish;}iterator insert(iterator pos, const T& x) //給返回值是解決迭代器失效{assert(pos >= _start);assert(pos <= _finish);if (_finish == _endofstorage){size_t len = pos - _start;reserve(capacity() == 0 ? 4 : capacity() * 2);// 擴容后這里需要更新pos,否則pos就是野指針了pos = _start + len;}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;--end;}*pos = x;++_finish;return pos;}iterator erase(const iterator pos)  //給返回值是解決迭代器失效{assert(pos >= _start);assert(pos < _finish);iterator it = pos + 1;while (it != _finish){*(it - 1) = *it;++it;}return pos;}bool empty() const{return _start == _finish;}void resize(size_t n, T val = T()){if (n > size()){reserve(n);while (_finish != _start + n){*_finish = val;++_finish;}}else{_finish = _start + n;}}private:iterator _start = nullptr;iterator _finish = nullptr;iterator _endofstorage = nullptr;};
}

完。

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

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

相關文章

【力扣】關于鏈表索引

怎么才能走到目標節點呢&#xff1f; 從9走到2&#xff0c;需要2步&#xff0c;他們的索引分別是&#xff1a;0&#xff0c;2 在for循環里&#xff1a;int i 0; i < 2; i i的范圍是【0&#xff0c;2&#xff09; 有&#xff1a;2 2 - 0 如果從虛擬頭節點開始走到2&#x…

C++ ODB框架詳解:現代C++對象關系映射解決方案

目錄 框架簡介安裝與配置基礎概念實體映射數據庫操作查詢操作高級功能性能優化最佳實踐 框架簡介 ODB&#xff08;Object-Relational Database&#xff09;是一個專為C設計的對象關系映射&#xff08;ORM&#xff09;框架&#xff0c;由CodeSynthesis公司開發。它提供了一種…

Ai書簽管理工具開發全記錄(一):項目總覽與技術藍圖

文章目錄 Ai書簽管理工具開發全記錄&#xff08;一&#xff09;&#xff1a;項目總覽與技術藍圖 ?1. 項目背景與核心價值 &#x1f4a1;1.1. 核心特點 2. 技術架構分析 &#x1f3d7;?功能架構全景圖典型工作流 3. 核心技術棧選擇 &#x1f6e0;?4. 預期使用功能說明 &#…

GUI 編程——python

GUI 編程核心概念 GUI&#xff08;圖形用戶界面&#xff0c;Graphical User Interface&#xff09; 是一種通過圖形元素&#xff08;窗口、按鈕、菜單等&#xff09;與用戶交互的應用程序形式&#xff0c;相比命令行界面更直觀易用。以下是學習 GUI 編程的基礎概念和流程&…

【Doris基礎】Apache Doris 基本架構深度解析:從存儲到查詢的完整技術演進

目錄 1 引言 2 Doris 架構全景圖 2 核心組件技術解析 2.1 Frontend 層&#xff08;FE&#xff09; 2.2 Backend 層&#xff08;BE&#xff09; 3 數據存儲與復制機制 3.1 存儲架構演進 3.2 副本復制策略 4 查詢處理全流程解析 4.1 查詢生命周期 5 高可用設計 5.1 F…

光電賦能低空場景,靈途科技助力無人機持續升級

2025 UASE 主題為“步入低空經濟新時代”的“2025第九屆世界無人機大會暨國際低空經濟與無人系統博覽會/第十屆深圳國際無人機展覽會”5月23日在深圳會展中心隆重開幕。本屆展會匯聚了全球800余家企業參展&#xff0c;展示5000多款無人機及系統設備&#xff0c;全面呈現低空經…

iOS QQ抽屜式導航的實現

QQ個人中心的側滑功能(通常稱為"抽屜式導航")可以通過以下幾種方式在iOS中實現&#xff1a; 主要實現方案 使用第三方庫 最快速的方式是使用成熟的第三方庫&#xff1a; SWRevealViewController&#xff1a;最流行的側滑菜單庫MMDrawerController&#xff1a;另一…

【Pandas】pandas DataFrame drop

Pandas2.2 DataFrame Reindexing selection label manipulation 方法描述DataFrame.add_prefix(prefix[, axis])用于在 DataFrame 的行標簽或列標簽前添加指定前綴的方法DataFrame.add_suffix(suffix[, axis])用于在 DataFrame 的行標簽或列標簽后添加指定后綴的方法DataFram…

長短期記憶網絡 (LSTM) 詳解:從原理到應用

一、引言&#xff1a;序列數據處理的挑戰? 在自然語言處理、語音識別、時間序列分析等領域&#xff0c;數據通常以序列形式存在&#xff0c;前后數據點之間存在依賴關系。傳統循環神經網絡 (RNN) 雖然能捕捉序列依賴&#xff0c;但存在嚴重的梯度消失 / 爆炸問題&#xff0c;…

三天掌握PyTorch精髓:從感知機到ResNet的快速進階方法論

本文較長&#xff0c;建議點贊收藏&#xff0c;以免遺失。更多AI大模型應用開發學習視頻及資料&#xff0c;盡在聚客AI學院。 一、分析式AI基礎與深度學習核心概念 1.1 深度學習三要素 數學基礎&#xff1a; f(x;W,b)σ(Wxb)(單層感知機) 1.2 PyTorch核心組件 張量操作示例…

Linux操作系統概述

一、操作系統的作用 1、五大基本功能 &#xff08;1&#xff09;進程和線程的管理&#xff1a;進程線程的狀態、控制、同步互斥、通信調度等 (2&#xff09;存儲管理&#xff1a;分配/回收、地址轉換、存儲保護等 (3&#xff09;文件管理&#xff1a;文件目錄、文件操作、磁盤…

Python爬蟲第22節- 結合Selenium識別滑動驗證碼實戰

目錄 一、引言 二、滑動驗證碼原理與反爬機制 2.1 驗證碼原理 2.2 反爬機制 三、工程實戰&#xff1a;滑動驗證碼識別全流程 3.1 工程準備 3.1.1 環境依賴 3.1.2 目標網站與驗證碼識別案例 3.2 核心破解流程 3.2.1 自動化打開網頁與登錄 3.2.2 獲取驗證碼圖片&#…

NSSCTF-[NISACTF 2022]huaji?

下載附件得到文件 放到kali里面看看 發現是一張圖片 用binwalk命令對其進行分離 發現需要密碼 用010打開圖片進行查看 對其進行解密 分別得到 ctf_NISA_2022 nisa_2022 發現ctf_NISA_2022是密碼 得到flag NSSCTF{Nls_FumYEnnOjy}

nt!CcGetVacbMiss函數分析之設置好nt!_VACB然后調用函數nt!SetVacb

第一部分&#xff1a;MmMapViewInSystemCache函數返回 Status MmMapViewInSystemCache (SharedCacheMap->Section, &Vacb->BaseAddress, &NormalOffset, …

Uniapp+UView+Uni-star打包小程序極簡方案

一、減少主包體積 主包污染源&#xff08;全局文件依賴&#xff09;勁量獨立導入 componentsstaticmain.jsApp.vueuni.css 分包配置缺陷&#xff0c;未配置manifest.json中mp-weixin節點 "usingComponents" : true,"lazyCodeLoading" : "requiredC…

Teigha應用——解析CAD文件(DWG格式)Teigha在CAD C#二次開發中的基本應用

Teigha是一款專為開發者設計的工具&#xff0c;其核心技術在于強大的API和豐富的功能集&#xff0c;提供了一系列工具和方法&#xff0c;使開發者能夠輕松地讀取、解析和操作DWG文件。它支持多種操作系統&#xff0c;能在處理大型DWG文件時保持高效性能&#xff0c;還可用于構建…

JavaWeb:SpringBoot Bean管理

獲取Bean Bean作用域 解決循環依賴方式 1.粗暴刪除依賴 2.打破依賴配置 3.使用lazy注解 引入第三方Bean

Lua 腳本在 Redis 中的運用-23(Lua 腳本語法教程)

在 Redis 中編寫和執行 Lua 腳本 Lua 腳本是在 Redis 中執行自定義邏輯的強大功能&#xff0c;可以直接在 Redis 服務器上執行。這減少了延遲&#xff0c;提高了性能&#xff0c;并能夠實現客戶端腳本難以或不可能實現的原子操作。通過在 Redis 中嵌入 Lua 腳本&#xff0c;您…

從零實現本地語音識別(FunASR)

FunASR 是達摩院開源的綜合性語音處理工具包&#xff0c;提供語音識別&#xff08;ASR&#xff09;、語音活動檢測&#xff08;VAD&#xff09;、標點恢復&#xff08;PUNC&#xff09;等全流程功能&#xff0c;支持多種主流模型&#xff08;如 Paraformer、Whisper、SenseVoic…

deepseek開源資料匯總

參考&#xff1a;DeepSeek“開源周”收官&#xff0c;連續五天到底都發布了什么? 目錄 一、首日開源-FlashMLA 二、Day2 DeepEP 三、Day3 DeepGEMM 四、Day4 DualPipe & EPLB 五、Day5 3FS & Smallpond 總結 一、首日開源-FlashMLA 多頭部潛在注意力機制&#x…