C++實(shí)現(xiàn)list增刪查改模擬的示例代碼
前言
本篇博客采用SGI版本,同時(shí)考慮到看到此篇博客大部分為初學(xué)者,為此博主僅僅給出有用片段。C++:list增刪查改模擬實(shí)現(xiàn)就是用C++復(fù)寫雙鏈表,非常簡(jiǎn)單。難點(diǎn)主要在迭代器實(shí)現(xiàn)
一、list底層雙鏈表驗(yàn)證、節(jié)點(diǎn)構(gòu)造
1.1 list底層數(shù)據(jù)結(jié)構(gòu)
list底層使用什么數(shù)據(jù)結(jié)構(gòu)來(lái)實(shí)現(xiàn)的呢?我們先來(lái)看看SGI庫(kù)中l(wèi)ist的成員函數(shù)和初始化吧。
我們發(fā)現(xiàn)list實(shí)現(xiàn)中,只有node一個(gè)成員變量。構(gòu)造函數(shù)構(gòu)造出一個(gè)頭尾相連的哨兵位。同時(shí)驗(yàn)證了list底層是一個(gè)帶哨兵位的雙鏈表。
1. 2 節(jié)點(diǎn)構(gòu)造
節(jié)點(diǎn)和雙鏈表一樣有三個(gè)成員:節(jié)點(diǎn)數(shù)據(jù)、指向前一個(gè)節(jié)點(diǎn)(prev)、指向后一個(gè)節(jié)點(diǎn)(next)。
//節(jié)點(diǎn) template<class T> struct List_node { T _data; List_node<T>* _prev; List_node<T>* _next; List_node(const T& x = T()) :_data(x) ,_prev(nullptr) ,_next(nullptr) {} };
小tips:
- 我們這里類名和庫(kù)中一樣(List_node),后續(xù)在其他類中使用時(shí)在typedef。
- 這里類名使用struct而不是class原因在于struct默認(rèn)訪問(wèn)權(quán)限為公有,后續(xù)其他類只都需要大量使用。如果使用class需要使用大量友元類,過(guò)于繁瑣。
二、迭代器封裝實(shí)現(xiàn)(重點(diǎn)、難點(diǎn))
2.1 前置說(shuō)明
- 我們知道迭代器的最大用途便是遍歷數(shù)據(jù)。但何時(shí)停在,迭代器指向空間存儲(chǔ)數(shù)據(jù)時(shí)是什么…導(dǎo)致我們需要使用!=、++、*等操作。但自定義類型是無(wú)法支持使用這些操作符的。為此給出的解決辦法是:封裝加運(yùn)算符重載。
- 迭代器使用上分為普通迭代器和const迭代器(還分為單向迭代器、雙向迭代器和隨機(jī)訪問(wèn)迭代器)。其中一種最簡(jiǎn)單的實(shí)現(xiàn)方式就是實(shí)現(xiàn)兩個(gè)類。但。。。我們知道兩個(gè)迭代器的不同之處在于const迭代器無(wú)法修改數(shù)據(jù),只是j相關(guān)借口(這里有*、->)不同而已便實(shí)現(xiàn)兩個(gè)類未免過(guò)于“小題大做”。
所以接下來(lái)我們來(lái)看看庫(kù)中是如何巧妙的解決這個(gè)問(wèn)題吧!
2.2 迭代器實(shí)現(xiàn)
前置說(shuō)明中以及解決了如何實(shí)現(xiàn)一個(gè)類達(dá)到目的。接下來(lái)的實(shí)現(xiàn)過(guò)于簡(jiǎn)單就不單獨(dú)說(shuō)明了。
//迭代器封裝 template<class T, class Ref, class Ptr> struct __list_iterator { typedef List_node<T> Node;//節(jié)點(diǎn)類名重命名 //由于迭代器實(shí)現(xiàn)中,如++、--等重載函數(shù)返回值類型為_(kāi)_list_iterator,名字過(guò)長(zhǎng),這里我們重命名self意味自身 typedef __list_iterator<T,Ref, Ptr> self; Node* _node;//成員變量 __list_iterator(Node* node)//構(gòu)造出一個(gè)節(jié)點(diǎn) :_node(node) {} //前置++ self& operator++() { _node = _node->_next; return *this; } //前置-- self& operator--() { _node = _node->_prev; return *this; } //后置++ self operator++(int) { self tmp(*this); _node = _node->_next; return tmp; } //后置-- self operator--(int) { self tmp(*this); _node = _node->_prev; return tmp; } //解引用操作符重載 Ref operator*() { return _node->_data; } //用于訪問(wèn)迭代器指向?qū)ο笾写鎯?chǔ)的是自定義類型 Ptr operator->() { return &_node->_data; } bool operator!=(const self& s) { return _node != s._node; } bool operator==(const self& s) { return _node == s._node; } };
三、list實(shí)現(xiàn)
3.1 基本框架
list模擬中,我們和庫(kù)中一樣,給出一個(gè)頭節(jié)點(diǎn)_head、_size兩個(gè)成員變量。同時(shí)我們將節(jié)點(diǎn)、迭代器進(jìn)行重命名。迭代器重命名不是單純圖方便,更重要的是提供統(tǒng)一接口(例如sting、vector、set等接口都一樣),屏蔽底層的變量和成員函數(shù)屬性,實(shí)現(xiàn)過(guò)程和差異。
//list模擬實(shí)現(xiàn) template<class T> class List { typedef List_node<T> Node; public: //迭代器重命名,提供統(tǒng)一的接口,屏蔽底層實(shí)現(xiàn)細(xì)節(jié)和差異 typedef __list_iterator<T, T&, T*> iterator; typedef __list_iterator<T, const T&, const T*> const_iterator; private: Node* _head;//頭節(jié)點(diǎn) int _size; };
3.2 迭代器和const迭代器
下面是begin()、end()指向一個(gè)有效雙線表的位置。
實(shí)現(xiàn):
const_iterator begin()const { //return const_iterator(_head->_next);或 return _head->_next;//單參數(shù)類型支持隱式類型轉(zhuǎn)換 } const_iterator end()const { return _head; } iterator begin() { return _head->_next; } iterator end() { return _head; }
3.2 構(gòu)造函數(shù)、析構(gòu)函數(shù)、拷貝構(gòu)造、賦值重載
3.2.1 構(gòu)造函數(shù)
構(gòu)造函數(shù)的實(shí)現(xiàn)和開(kāi)頭中看到的一樣:構(gòu)造函數(shù)中調(diào)用一個(gè)函數(shù)(empty_Init)來(lái)是實(shí)現(xiàn)。empty_Init()用來(lái)完成初始化。(empty_Init()函數(shù)后續(xù)其他接口也要調(diào)用)
//初始化 void empty_Init() { _head = new Node; _head->_next = _head; _head->_prev = _head; _size = 0; } //無(wú)參構(gòu)造 List() { empty_Init(); }
3.2.2 析構(gòu)函數(shù)
析構(gòu)函數(shù)我們調(diào)用一個(gè)clear函數(shù)來(lái)將數(shù)據(jù)全部清空,在將_head變量釋放。
//析構(gòu)函數(shù) ~List() { clear(); delete _head; _head = nullptr; } void clear() { iterator it = begin(); while (it != end()) { it = erase(it); } }
3.2.3 拷貝構(gòu)造
拷貝構(gòu)造時(shí)首先要初始化出一個(gè)節(jié)點(diǎn),然后將需要拷貝的數(shù)據(jù)依次尾插即可(尾插接口后續(xù)給出實(shí)現(xiàn))
//拷貝構(gòu)造 List(const List<T>& It) { empty_Init(); for (auto e : It) { push_back(e); } }
3.2.4 賦值重載
賦值重載有很多方式。比較簡(jiǎn)單的參數(shù)我們直接傳值,然后將待賦值的變量和傳值傳參省生成的臨時(shí)變量的數(shù)據(jù)進(jìn)行交換即可。
void swap(const List<T>& It) { std::swap(_head, It._head); } //賦值重載 List<T>& operator=(const List<T> It) { swap(It); return *this; }
3.3 任意位置插入、任意位置刪除、尾插、尾刪、頭插、頭刪
3.3.1任意位置插入
首先new出待插入節(jié)點(diǎn)newnode,然后將pos前一個(gè)節(jié)點(diǎn)prev、newnode、pos相連。最后++_size即可。
iterator insert(iterator pos, const T& x) { Node* cur = pos._node; Node* prev = cur->_prev; Node* newnode = new Node(x); prev->_next = newnode; newnode->_prev = prev; newnode->_next = cur; cur->_prev = newnode; _size++; return newnode; }
3.3.2任意位置插入刪除
將待刪除數(shù)據(jù)的前后節(jié)點(diǎn)先保存起來(lái),然后將刪除pos處節(jié)點(diǎn),再將前后節(jié)點(diǎn)連接起來(lái)。最后–_size即可。
iterator erase(iterator pos) { Node* cur = pos._node; Node* prev = cur->_prev; Node* next = cur->_next; delete cur; prev->_next = next; next->_prev = prev; _size--; return next; }
3.3.3 尾插、尾刪、頭插、頭刪
尾插、尾刪、頭插、頭刪都是復(fù)用任意位置插入、任意位置刪除接口。
void push_back(const T& x) { insert(end(), x); } void push_front(const T& x) { insert(begin(), x); } void pop_back() { erase(--end()); } void pop_front() { erase(begin()); }
四、list功能完善
4.1 迭代器operator->()
我們先來(lái)看看下面這段代碼對(duì)嗎?
struct AA { AA(int a1 = 0, int a2 = 0) :_a1(a1) ,_a2(a2) {} int _a1; int _a2; }; void test_list3() { list<AA> lt; lt.push_back(AA(1, 1)); lt.push_back(AA(2, 2)); lt.push_back(AA(3, 3)); list<AA>::iterator it = lt.begin(); while (it != lt.end()) { cout<<*it<<" "; ++it; } cout << endl; }
由于list沒(méi)有重載<<,所以對(duì)存儲(chǔ)的是自定義類型之間訪問(wèn)會(huì)編譯報(bào)錯(cuò)。
那我們重載下<<運(yùn)算符不就行了嗎?很不幸的是C++庫(kù)在list中不支持<<,很大原因也在于編譯器不知到我們?nèi)绾稳?shù)據(jù)
所以對(duì)于自定義類型我們可以先解引用在去訪問(wèn)成員,也可以在迭代器中重載operator->()函數(shù)。但需要注意的是編譯器隱藏了一個(gè)->,具體原生行為如下:
struct AA { AA(int a1 = 0, int a2 = 0) :_a1(a1) ,_a2(a2) {} int _a1; int _a2; }; void test_list3() { list<AA> lt; lt.push_back(AA(1, 1)); lt.push_back(AA(2, 2)); lt.push_back(AA(3, 3)); list<AA>::iterator it = lt.begin(); while (it != lt.end()) { //cout << (*it)._a1<<" "<<(*it)._a2 << endl; cout << it->_a1 << " " << it->_a2 << endl; //上面編譯器訪問(wèn)成員變量的真正行為如下: //cout << it.operator->()->_a1 << " " << it.operator->()->_a1 << endl; ++it; } cout << endl; }
4.2 打印數(shù)據(jù)
//大多數(shù)情況模板是class還是typename是一樣的,但當(dāng)有未實(shí)例化模板時(shí),必須使用typename //template<typename T> void print_list(const list<T>& lt) { // list<T>未實(shí)例化的類模板,編譯器不能去他里面去找 // 編譯器就無(wú)法list<T>::const_iterator是內(nèi)嵌類型,還是靜態(tài)成員變量 // 前面加一個(gè)typename就是告訴編譯器,這里是一個(gè)類型,等list<T>實(shí)例化 // 再去類里面去取 typename list<T>::const_iterator it = lt.begin(); while (it != lt.end()) { cout << *it << " "; ++it; } cout << endl; }
優(yōu)化:上面打印數(shù)據(jù)是針對(duì)list,下面是針對(duì)容器的。
// 模板(泛型編程)本質(zhì),本來(lái)應(yīng)該由我們干的活交給編譯器 template<typename Container> void print_container(const Container& con) { typename Container::const_iterator it = con.begin(); while (it != con.end()) { cout << *it << " "; ++it; } cout << endl; }
五·、所有代碼以及測(cè)試用例
giteeC++:list增刪查改模擬實(shí)現(xiàn)代碼以及測(cè)試用例
推薦:giteeC++:list增刪查改模擬實(shí)現(xiàn)代碼(最終版本、感覺(jué)版本?。。。?/a>
到此這篇關(guān)于C++實(shí)現(xiàn)list增刪查改模擬的文章就介紹到這了,更多相關(guān)C++ list增刪查改 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++通過(guò)SQLiteSDK增刪改查的實(shí)現(xiàn)示例
SQLite是一種輕量級(jí)的嵌入式數(shù)據(jù)庫(kù),可以利用SQLiteSDK執(zhí)行數(shù)據(jù)庫(kù)的增刪改查操作,本文主要介紹了C++通過(guò)SQLiteSDK增刪改查,具有一定的參考價(jià)值,感興趣的可以了解一下2025-03-03C++使用string的大數(shù)乘法運(yùn)算(3)
這篇文章主要為大家詳細(xì)介紹了C++使用string的大數(shù)乘法運(yùn)算,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-09-09C++ 標(biāo)準(zhǔn)模板庫(kù) STL 順序容器詳解
這篇文章主要介紹了C++ 標(biāo)準(zhǔn)模板庫(kù) STL 順序容器詳解,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-05-05Ubuntu20.04安裝使用jsoncpp、json-c庫(kù)的方法實(shí)例
這篇文章主要給大家介紹了關(guān)于Ubuntu20.04安裝使用jsoncpp、json-c庫(kù)的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作就有一定的參考借鑒價(jià)值,需要的朋友可以參考下2024-04-04C語(yǔ)言 棧的表示和實(shí)現(xiàn)詳細(xì)介紹
這篇文章主要介紹了C語(yǔ)言 棧的表示和實(shí)現(xiàn)詳細(xì)介紹的相關(guān)資料,需要的朋友可以參考下2016-12-12Qt數(shù)據(jù)庫(kù)應(yīng)用之實(shí)現(xiàn)通用數(shù)據(jù)庫(kù)請(qǐng)求
這篇文章主要為大家介紹了Qt中是如何實(shí)現(xiàn)通用數(shù)據(jù)庫(kù)請(qǐng)求的,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Qt有一定幫助,感興趣的小伙伴可以了解一下2022-03-03