C++ vector 模擬實現

vector的底層也是一個動態數組,他與 string 的區別就是,string 是專門用來存儲字符類數據的,為了兼容C語言,使用C語言的接口,在string的動態數組內都會都開一塊空間用來存 \0? ,而vector則不會。

首先我們要知道的就是,vector是一個類模板,他的內存管理是使用空間配置器,我們就簡單點直接使用new和delete來管理內存。其他的一些接口什么的,vector與string差別不大,vector有的string基本都有,實現起來我們也挑一些重點來實現。而對于vector的成員變量,我們則不是采用string的一個指針兩個整型的實現,而是使用三個迭代器,_start,_finish,_end_of_storage,這是參考Linux的庫的實現,而我們實現vector時迭代器就直接使用的原生的指針。

_start 迭代器是數組的開頭,_finish是最后一個數據的下一個位置,_end_of_storage指向的是申請的內存塊的下一個位置。

使用三個迭代器的實現,在后續需要挪數據的時候會很方便。

無參構造函數與析構

無參構造不用說,很簡單也沒什么含量,都初始化為空指針就行了

		vector():_start(nullptr),_finish(nullptr),_end_of_storage(nullptr){}

完成了無參構造函數之后,我們先去實現其他的一些基本的函數,最后再來解決其他的三個構造

析構函數就是釋放_start指向的空間

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

size與capacity和判空

size與capacity的實現其實很簡答,我們之前學過數組中同類型指針相減,得到的是這兩個指針之間的該類型的數據的個數,于是我們就可以用這三個迭代器來得到size和capacity

	    //sizesize_t size()const{return _finish - _start;}//capacitysize_t capacity()const{return _end_of_storage - _start;}//判空bool empty()const{return _start == _finish;}

方括號的重載與at

vector由于還是動態數組實現的,所以我們還是支持直接是用下標訪問的方式,所以方括號重載的實現還是有必要的,方括號重載的越界檢查我們直接使用assert暴力檢查,庫里面實現的就是assert。同時at的功能雖然和方括號一樣,但是他和方括號的區別在于 : 1 方括號是運算符重載,而at是普通的成員函數。2 越界的檢查不一樣,方括號的越界檢查使用assert,而at是拋異常,我們可使用vs的庫試一下

由于我們還沒學習異常。也直接用assert判斷就行了。同時,由于有普通對象和const對象的訪問,返回的引用的權限不一樣,所以我們需要實現兩個版本

		//普通對象  [ ]reference operator[](size_t pos){assert(pos < size());return _start[pos];}//const對象 [ ] const_reference operator[](size_t pos)const{assert(pos < size());return _start[pos];}//普通對象 atreference at(size_t pos){assert(pos < size());return _start[pos];}//const對象 atconst_reference at(size_t pos)const{assert(pos < size());return _start[pos];}

對于下標為pos位置的數據的訪問,我們可以直接使用 _start[pos]來訪問,等價于*(_start+pos)

由于vector也就是普通的數組的比較是沒有意義的,所以一般只重載[ ] 和=

swap

為什么要實現swap呢?

首先vector的庫里面是有swap的,用于交換兩個對象的數據,但是算法庫里面也有swap,

他們的區別在于,算法庫里的swap有三次拷貝構造,如果要交換的是兩個自定義類型的對象,代價很大,而我們的類里面的成員函數swap,我們可以使用算法庫的swap對對象里的成員變量進行交換,避免了深拷貝。

//swapvoid swap(rvector<T>& v){//要使用算法庫里面的swap要指定命名空間域,否則會優先匹配當前命名空間的swap函數std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_end_of_storage, v._end_of_storage);}

同時我們實現swap也是為了下面的拷貝構造和賦值重載做準備

賦值重載

vector的賦值重載顯然是深拷貝,我們有一種很簡潔的代碼寫法來完成這個深拷貝,就是利用拷貝構造,我們可以傳值傳參,將我們的this與生成的臨時的形參進行交換

而當v2不為空時也是一樣的,無非就是將這臨時對象與當前對象的空間換了一下,出作用域之后,臨時對象指向的空間就被釋放了,不會影響外面等號右邊的變量。

        //賦值重載vector<T>& operator=(vector<T> v){swap(v);return *this;}

push_back和pop_back

尾插的實現就很簡單了,首先檢查是否擴容,然后直接在 _finish位置插入數據并且更新 _finish就行了。而尾刪的操作則是判斷是否為空,然后直接 -- _finish 就行了。

		void push_back(const_reference x){if (_finish == _end_of_storage){size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();reserve(newcapacity);}*_finish = x;;++_finish;//insert(beign(),x);//復用insert}void pop_back(){assert(size()>0);--_finish;}

拷貝構造

我們需要考慮當 T 為需要深拷貝的自定義類型這種情況,要做深層次的深拷貝。

如果數據只是內置類型或者只需要淺拷貝的自定義類型的話,我們就只需要memcpy就行了,但是如果是需要深拷貝的數據的話,比如 vector<vector<int>> ,數據類型是 vector<int> ,需要深拷貝,如果只是簡單的memcpy

	//如果只是內置類型或者淺拷貝自定義類型//先擴容reserve(v.capacity());//函數內部會修改_end_of_storage//直接內存拷貝memcpy(_start,v._start,v.size()*sizeof(T));_finish = _start + v.size();

我們發現這時候兩個對象雖然是不同的空間,但是里面的 vector<int> 數據指向的空間是同一塊,這其實還沒有達到我們想要的深拷貝。

對于我們來說目前也沒有什么更好的解決方法,可以利用上面實現的賦值重載,賦值重載去對每一個數據都深拷貝,

//拷貝構造vector(const vector<T>& v):_start(nullptr),_finish(nullptr),_end_of_storage(nullptr){//先擴容reserve(v.capacity());//或者先擴size,再使用賦值重載進行深拷貝_finish = _start + v.size();for (size_t i = 0; i < size(); ++i){_start[i] = v[i];}}

同時,拷貝構造完成之后,不管再多層,他們都有對應的拷貝構造,但是可能有人會對這里的賦值重載產生疑惑,認為賦值重載的前提是實現拷貝構造,而我們這里的拷貝構造又用了賦值重載,會不會遞歸死循環了,其實是不會的,就比如這里的vector<vector<int>> ,會生成 vector<int> 和vector<vector<int>>的模板,而在vector<vector<int>>中拷貝構造的賦值重載是屬于 vector<int>實例出來的賦值重載,而這個賦值重載調用的是 int?的拷貝構造,也就是內置類型的傳值調用,所以這里的邏輯是沒有問題的,拷貝構造和賦值重載的互相調用會在內置類型(或者淺拷貝的自定義類型)停下來,然后回歸。總之就是 賦值重載的深拷貝是調用 T 類型的拷貝構造

reserve

reserve函數與string一樣,只會擴容,不會縮容,同時不會改變size。但是擴容之后需要解決將原空間的數據拷貝到新空間的問題,我們可以直接使用指針來進行邊界控制。

但是同時,我們也要考慮 數據類型是需要深拷貝的自定義類型的情況,比如 vector<vector<int>> ,如果只是單純的memcpy,也會造成淺拷貝的問題,也就是原空間的 vector<int>對象與新空間的vector<int>對象指向相同的內存空間,當時放掉原空間之后,新空間指向的內存也被釋放掉了。

解決方法還是跟上面的深拷貝一樣,使用數據自身的賦值重載來拷貝,雖然效率低了點,但是能確保不會出問題。

迭代器相關

		//迭代器iterator begin(){return _start;}iterator end(){return _finish;}const iterator cbegin(){return _start;}const iterator cend(){return _finish;}

insert和erase

		iterator insert(iterator pos, const_reference x=val_type())//缺省值{//檢查越界assert(pos >= _start);assert(pos <= _finish);//第一次插入pos==_finish  同時要支持尾插//檢查是否擴容if (_finish == _end_of_storage){//擴容之后pos就失效了,所以要記錄pos的相對位置size_t pos_index = pos - _start;size_t newcapacity = (capacity() == 0 ? 4 : 2 * capacity());reserve(newcapacity);//更新pospos = _start + pos_index;}//挪動數據 從后往前,往后挪數據iterator end = _finish;while (end != pos){*end = *(end - 1);--end;}//更新_finish++_finish;//插入新數據*pos = x;//返回插入的數據的迭代器return pos;}iterator erase(iterator pos){assert(pos >= _start);assert(pos < _finish);//挪數據覆蓋iterator begin = pos+1;while (begin < _finish){*(begin - 1) = *begin;begin++;}//返回刪除的下一個數據return pos;}

insert和erase都要注意迭代器失效的問題,比如insert的時候,如果擴容了,那么傳過來的pos指向的空間就可能已經被釋放了,所以要更新pos指向新空間的該元素的位置,同時,正常來說insert之后,函數外面的pos就不能用了,因為我們是傳值的,函數內更新了pos不會影響函數外的pos,這時候如果我們要更新函數外面的pos,可以insert返回心得pos的值。? erase也是一樣的,因為erase可能是刪除最后一個數據,這時候pos的位置就是_finish 了,雖然pos沒有出我們開辟的內存空間,但是在我們看來,它指向finish就已經算是越界了,不能繼續使用,同樣,要在函數外面更新pos也是用erase的返回值更新。

resize

		//resizevoid resize(size_t n, val_type x = val_type())//缺省參數{//擴容檢查if (n > capacity()){reserve(n);}//是否需要加數據if (n > size()){while (_finish < _start + n){*_finish=x;++_finish;}}}

剩下的兩個構造函數

一個是迭代器區間來構造初始化。為什么這里的構造函數是模板呢? 因為我們可能不是使用vector<T>的數據結構來初始化我們的對象,也可能是用其他的數據結構比如鏈表等的數據來初始化vector,這也是可以的,只要傳迭代器區間就可以

		template<typename InputIterator>vector(InputIterator begin, InputIterator end){//push_back就行了while (begin != end){push_back(*begin);++begin;}}

還有n個val初始化,這里我們需要注意的是,我們不能做到跟庫里一樣,n的參數類型要用int,為什么呢??

		//n個val初始化vector(int n, T val){for (int i = 0; i < n; ++i){push_back(val);}}

比如我們可以這樣構造 vector<int> v(10,5) 這時候,如果我們的n的類型是size_t 的話,由于編譯器會把 10 和5 識別為 int 類型,這時候傳給構造函數的兩個參數都是 int ,如果去匹配這個構造函數的話, n 還需要進行整型提升。而如果去匹配上面的迭代器區間的模板,因為模板的只有一個參數類型,兩個迭代器區域間都是一樣的類型,所以不用進行隱式類型轉換,因此他與迭代器區間的構造函數更加匹配,編譯器就會去調用迭代器區間的構造函數,這時候就會出問題。 有兩種解決方法,一種就是我們上面寫的,但是這樣做就與庫不一致了 。另外一種就是我們不要使用原生的指針作為迭代器的類型,這種方法我們目前還不會用

完整代碼


namespace MY_vector
{template<typename T>class vector{public:typedef T val_type;typedef T& reference;typedef const T& const_reference;typedef T* iterator;//無參構造vector():_start(nullptr),_finish(nullptr),_end_of_storage(nullptr){}//析構~vector(){delete[] _start;_start = _finish = _end_of_storage=nullptr;}//sizesize_t size()const{return _finish - _start;}//capacitysize_t capacity()const{return _end_of_storage-_start;}//emptybool empty()const{return _start == _finish;}//尾插void push_back(val_type x){ /*//檢查擴容if (_finish== _finish){size_t newcapacity = (capacity() == 0 ? 4 : 2 * capacity());reserve(newcapacity);}//插入數據*_finish = x;++_finish;*/insert(begin(), x);}//尾刪void pop_back(){assert(!empty());--_finish;}iterator insert(iterator pos, const_reference x=val_type())//缺省值{//檢查越界assert(pos >= _start);assert(pos <= _finish);//第一次插入pos==_finish  同時要支持尾插//檢查是否擴容if (_finish == _end_of_storage){//擴容之后pos就失效了,所以要記錄pos的相對位置size_t pos_index = pos - _start;size_t newcapacity = (capacity() == 0 ? 4 : 2 * capacity());reserve(newcapacity);//更新pospos = _start + pos_index;}//挪動數據 從后往前,往后挪數據iterator end = _finish;while (end != pos){*end = *(end - 1);--end;}//更新_finish++_finish;//插入新數據*pos = x;//返回插入的數據的迭代器return pos;}iterator erase(iterator pos){assert(pos >= _start);assert(pos < _finish);//挪數據覆蓋iterator begin = pos+1;while (begin < _finish){*(begin - 1) = *begin;begin++;}//返回刪除的下一個數據return pos;}//[ ] 重載reference operator[](size_t pos){assert(pos < size());return _start[pos];}const_reference operator[](size_t pos)const{assert(pos < size());return _start[pos];}//atreference at(size_t pos){assert(pos<size());return _start[pos];}const_reference at(size_t pos)const{assert(pos < size());return _start[pos];}//swapvoid swap(vector<T>& v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_end_of_storage, v._end_of_storage);}//= 重載vector<T>& operator=(vector<T> v){swap(v);return *this;}//擴容 reservevoid reserve(size_t n){if (n > capacity()){iterator tmp = new val_type[n];//記錄原來的數據個數,用來更新_finishsize_t oldsize = size();//判斷是否需要挪數據if(_start) //_start!=nullptr{for (size_t i = 0; i < oldsize; ++i){tmp[i] = _start[i];}//釋放原空間 delete[]_start;}//更新迭代器_start = tmp;_finish = _start + oldsize;_end_of_storage = _start + n;}}//拷貝構造vector(const vector<T>& v):_start(nullptr),_finish(nullptr),_end_of_storage(nullptr){//先擴容reserve(v.capacity());//或者先擴size,再使用賦值重載進行深拷貝_finish = _start + v.size();for (size_t i = 0; i < size(); ++i){_start[i] = v[i];}}//resizevoid resize(size_t n, val_type x = val_type())//缺省參數{//擴容檢查if (n > capacity()){reserve(n);}//是否需要加數據if (n > size()){while (_finish < _start + n){*_finish=x;++_finish;}}}//迭代器iterator begin(){return _start;}iterator end(){return _finish;}const iterator cbegin(){return _start;}const iterator cend(){return _finish;}template<typename InputIterator>vector(InputIterator begin, InputIterator end){//push_back就行了while (begin != end){push_back(*begin);++begin;}}//n個val初始化vector(int n, T val){for (int i = 0; i < n; ++i){push_back(val);}}private:iterator _start;iterator _finish;iterator _end_of_storage;};//測試一維void test1(){vector<int>v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);v1.push_back(5);vector<int> v2(v1);vector<int>v3;v3.resize(8);v3 = v1;v3.reserve(10);v2.pop_back();v2.pop_back();v2.pop_back();v2.pop_back();//v2.pop_back();//v2.pop_back();//v2.pop_back();v3.insert(v3.begin(), 6);v3.insert(v3.begin(), 6);v3.insert(v3.end()-1, 6);v3.insert(v3.end() - 1, 6);v3.insert(v3.end(), 6);v3.insert(v3.end(), 6);for (auto e : v1){cout << e << " ";}cout << endl;for (auto e : v2){cout << e << " ";}cout << endl;for (auto e : v3){cout << e << " ";}cout << endl;}//測試二維void test2(){vector<int>v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);vector<vector<int>> vv1;vv1.push_back(v);vv1.push_back(v);vv1.push_back(v);vv1.push_back(v);vv1.push_back(v);vector<vector<int>> vv2(vv1);vv2.pop_back();vv2.pop_back();vector<vector<int>> vv3;vv3.resize(2);vv3.reserve(6);vv3.push_back(v);vv3.push_back(v);vv3 = vv1;vv3.push_back(v);vv3.push_back(v);cout << "vv1" << endl;for (auto ev : vv1){for (auto e : ev){cout << e << " ";}cout << endl;}cout << "vv2" << endl;for (auto ev : vv2){for (auto e : ev){cout << e << " ";}cout << endl;}cout << "vv3" << endl;for (auto ev : vv3){for (auto e : ev){cout << e << " ";}cout << endl;}}
}

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

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

相關文章

【Linux多線程】認識多線程創建線程

文章目錄 什么是多線程為什么稱linux下的線程是輕量級進程呢&#xff1f; 線程的優點線程的缺點線程異常線程和進程創建線程1.pthread_create2.pthread_self 什么是多線程 進程是正在運行的程序的實例&#xff0c;而線程&#xff08;thread&#xff09;是進程中的一個執行路線…

python 刪除pdf 空白頁

環境 python 3.10 PyPDF2 3.0.1 安裝 pip install PyPDF2流程 將空白頁和內容頁讀取出來&#xff0c;看看內部結構有什么不同以此為依據&#xff0c;遍歷整個PDF 文件&#xff0c;標記處有內容的頁面&#xff0c;寫入到另外一個PDF文件。 python 代碼 # 每一個頁都是一個…

Springboot郵件發送配置

Springboot郵件發送配置 pom.xml依賴&#xff1a; <dependency><groupId>org.eclipse.angus</groupId><artifactId>jakarta.mail</artifactId><version>2.0.3</version> </dependency> <dependency><groupId>or…

跨域的解決方案

1. 計算機更改跨域 1.C盤->Windows->System32->drivers->etc 2.修改hosts 文件2. Chrome瀏覽器的跨域設置 操作步驟&#xff1a;1.打開我的電腦——C盤 新建一個文件夾&#xff0c;命名為MyChromeDevUserData2.右鍵——Chrome——快捷方式——目標&#xff0c;在…

ChatGPT成知名度最高生成式AI產品,使用頻率卻不高

5月29日&#xff0c;牛津大學、路透社新聞研究所聯合發布了一份生成式AI&#xff08;AIGC&#xff09;調查報告。 在今年3月28日—4月30日對美國、英國、法國、日本、丹麥和阿根廷的大約12,217人進行了調查&#xff0c;深度調研他們對生成式AI產品的應用情況。 結果顯示&…

ElementUI之el-table標題列中顯示el-tooltip

ElementUI之el-table標題列中顯示el-tooltip 文章目錄 ElementUI之el-table標題列中顯示el-tooltip1. el-table標題列中顯示el-tooltip2. 實現代碼3. 展示效果 1. el-table標題列中顯示el-tooltip 在el-table-column標簽內添加具名插槽v-slot:header 在el-tooltip標簽中使用具…

【幾何】輸入0-360度任意的角度,求上面直線與橢圓相切點的坐標計算公式

?輸入0-360度任意的角度,求上面直線與橢圓相切點的坐標計算公式 使用積分計算 使用到的公式有橢圓公式: x 2 a 2 + y 2 b 2 = 1 \frac{x^2}{a^2}+\frac{y^2}{b^2} = 1 a2x2?+b2y2?=1 平面旋轉公式 X r = cos ? θ ? ( X s ? X O ) ? sin ? θ ? ( Y s ? Y O ) + X …

端午節粽子龍舟主題互動趣味小游戲效果是什么

端午三天樂&#xff0c;無論節日當天還是之前&#xff0c;行業商家都可以自己的品牌為主借勢營銷&#xff0c;趣味活動形式玩法和內容呈現達成多種效果&#xff0c;品牌傳播、公眾號漲粉、線下互動、商品促銷、用戶促活等。 在【雨科】平臺擁有多款端午節互動小游戲類型&#…

網易狼人殺 設置點擊自動發言

我們玩網易狼人殺 剛開始 都會發現 要按住麥克風才能發言 不得不說 相當的麻煩 我們可以點擊如下圖 右上角這個設置的齒輪 新彈出的設置面板上 勾選這個點擊發言 然后 我們只需要 點一下 就可以進入發言狀態 然后 再點一下即可停止發言 會方便非常多

zabbix事件告警監控:如何實現對相同部件觸發器告警及恢復的強關聯

有一定Zabbix使用經驗的小伙伴可能會發現&#xff0c;接收告警事件時&#xff0c;其中可能包含著大量不同的部件名&#xff0c;同一部件的事件在邏輯上具有很強關聯性&#xff0c;理論上應保持一致的告警/恢復狀態&#xff0c;但Zabbix默認并未對它們進行關聯&#xff0c;直接后…

AIGC降重:如何2分鐘降低論文AI率和查重率?推薦使用SpeedAI科研小助手

確保學術論文的獨立性與誠信性&#xff0c;對于學業的成就及學位的獲取至關重要&#xff0c;其中&#xff0c;論文的人工智能查重與降低AIGC相似度扮演著核心角色。 常規的查重手段主要圍繞查重軟件的運用和個體的自行審查&#xff1b;而降重則通常通過語句重組、同義替換、內…

單細胞分析(Signac): PBMC scATAC-seq 基因組區域可視化

引言 在本教學指南中&#xff0c;我們將探討由10x Genomics公司提供的人類外周血單核細胞&#xff08;PBMCs&#xff09;的單細胞ATAC-seq數據集。 加載包 首先加載 Signac、Seurat 和我們將用于分析人類數據的其他一些包。 if (!requireNamespace("EnsDb.Hsapiens.v75&qu…

ModuleNotFoundError: No module named ‘osgeo‘

顯示無osgeo模塊 pip install osgeo顯示失敗 方法&#xff1a; 確保你已經安裝了正確的依賴項&#xff0c;例如GDAL、GEOS和PROJ等。 方法1&#xff1a;pip install gdal 失敗 方法2&#xff1a;官網下載失敗&#xff0c;下載地址&#xff1a;https://www.lfd.uci.edu/~gohl…

在Linux系統中,使用OpenSSL生成私有證書文件,并提取私鑰的步驟如下:

在Linux系統中&#xff0c;使用OpenSSL生成私有證書文件&#xff0c;并提取私鑰的步驟如下&#xff1a; 生成私鑰&#xff08;如果還沒有私鑰的話&#xff09;&#xff1a; openssl genpkey -algorithm RSA -out private.pem -pkeyopt rsa_keygen_bits:2048 生成自簽名證書&…

設置自動刷新數據透視表的數據源

數據透視表數據源的自動刷新 一般情況操作&#xff1a; 自動刷新操作&#xff1a; 1、定義名稱名稱 引用位置&#xff1a;OFFSET(Sheet1!$A$1,0,0,COUNTA(Sheet1!$A:$A),COUNTA(Sheet1!$1:$1)) 2、數據透視表的數據源更改為【源數據】—— 即前面定義的名稱 3、數據——全部…

Java多線程——觀測線程狀態

線程可以處于以下幾個狀態&#xff1a; NEW&#xff1a;尚未啟動的線程處于此狀態&#xff1b; RUNNABLE&#xff1a;在Java虛擬機中執行的線程處于此狀態&#xff1b; BLOCKED&#xff1a;被阻塞等待監視器鎖定的線程處于此狀態&#xff1b; WAITING&#xff1a;正在等待…

區塊鏈技術和應用二

前言 學習長安鏈的一些基本原理 官網&#xff1a;長安鏈開源文檔 b站課程&#xff1a;區塊鏈基礎與應用 一、共識算法 1.1 POW工作量證明 最長鏈共識&#xff0c;沒聽明白 1.2 51%攻擊 二、區塊鏈的發展 2.1 區塊鏈1.0到3.0 2.2 共有鏈、聯盟鏈、私有鏈 2.3 發展趨勢 2.4 擴…

CUDA_VISIBLE_DEVICES‘ 不是內部或外部命令,也不是可運行的程序或批處理文件。

問題&#xff1a; 命令行出現CUDA_VISIBLE_DEVICES0 python trainer.py這種命令 這是Linux可以的&#xff0c;但是Windows不行。 解決方案&#xff1a; 這條命令的含義是指定某個GPU來運行程序&#xff0c;我們可以在程序開頭添加指定GPU的代碼&#xff0c;效果是一樣的&…

HackTheBox-Machines--Lazy

Lazy測試過程 1 信息收集 1.端口掃描 發現 SSH&#xff08;22&#xff09;、HTTP&#xff08;80&#xff09;端口 nmap -sC -sV 10.129.159.512.訪問 80 端口 1.頁面中存在注冊功能&#xff0c;測試注冊功能 頁面返回登錄頁面及用戶名 使用burpsuite觀察注冊請求 /register.p…

Facebook:連接世界,暢游社交之旅

作為全球最大的社交平臺之一&#xff0c;Facebook不僅僅是一個網站&#xff0c;更是一個連接世界的橋梁&#xff0c;讓人們可以輕松地與全球各地的朋友、家人和同事保持聯系&#xff0c;分享生活、交流想法&#xff0c;暢游社交的無邊界之旅。本文將帶領讀者探索Facebook的魅力…