日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

STL模拟实现1.0 -- list和iterator模拟实现和简单分析

發布時間:2023/11/27 生活经验 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 STL模拟实现1.0 -- list和iterator模拟实现和简单分析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

引言

C ++ 標準模本庫《STL》中有很多優秀的代碼實現,不然怎么能叫做C++標準模板庫呢,其中一個實現就是有一個容器,叫做list。所謂容器其實就是存儲相同類型數據的一個存儲集合,list的底層實現其實就是一個鏈表。

我們的普通數組在使用的時候可以定義一個指針指向一個節點,然后使指針 ++ 就可以訪問下一個節點,我們想要我們的list也能夠使用這個功能,于是就出現了迭代器iterator,通過定義一個iterator,我們可以使用++訪問下一個節點,也可以使用*來找到iterator維護的節點的數據,本文就簡單模擬實現iterator。

iterator的使用

看下面的代碼

#include<iostream>
using namespace std;
#include<list>
void TestList()
{list<int> l;l.push_back(1);l.push_back(2);l.push_back(3);list<int>::iterator it = l.begin();while (it != l.end()){cout << *it << endl;it++;}
}

這個時候輸出的結果就是1 2 3,這樣使用list訪問數據是不是很方便呢,從上面的使用中,我們也可以終于理解迭代器到底是什么東西了,迭代器其實就是一個維護list中各個節點的一個東西,其中的begin()和end()放回的也是一個迭代器,那么迭代器到底是一個什么東西呢 ,我們呢通過下面的下面的模擬實現可以知道迭代器到底是什么。

簡單模擬實現list和iterator

talk is cheap,show you this code!
代碼的后面是模擬實現的分析哦,那才是重點

#include<iostream>
using namespace std;#include<list>template<class T>
struct _ListNode
{_ListNode<T>* _next;_ListNode<T>* _prev;T _data;_ListNode(T d = 0):_data(d), _next(NULL), _prev(NULL){}
};//這個T標記的是維護的數據類型,Ref是引用,這個引用是對數據的引用,就是這里面data的引用
//Ptr是指針,這個指針也是指向數據data的指針這兩個模板類型我們可以在
//List這個類里面使用,因為在編寫List_Iterator的時候,會經常使用Ref和Ptr
template<class T, class Ref, class Ptr>
class List_Iterator
{typedef _ListNode<T> Node;typedef List_Iterator<T, Ref, Ptr> Self;   //這個地方寫的 有點失誤了,就是把List_Iterator的單詞寫錯了
public:List_Iterator(Node* it):_it(it){}List_Iterator(const List_Iterator& it){_it = it._it;}//List_Iterator& operator++()Self& operator++(){_it = _it->_next;return *this;}Self operator++(int)    //注意前置++返回的是引用,而后置++返回的是Self{Node* tmp = _it;_it = _it->_next;return tmp;}//T& operator*()Ref operator*(){return _it->_data;}bool operator!=(const Self& it){return _it != it._it;    //一開始寫這個!=運算符重載的時候也出現了錯誤,就是我一開始是拿this和&it比//這顯然是不對的,兩個迭代器不可能是相等的,所以這個時候應該是拿//這兩個迭代器所指向的內容來比較,就是那他們的成員變量來 進行比較才正確}private:Node* _it;
};//這里我們需要做一個修改就是,我們需要把這個鏈表定義成一個雙向的有頭的循環鏈表
//這個工作就是要在構造函數的時候去做了
//所以這里_tail就不需要了//有頭結點,_head維護了一個頭結點,但是這個節點不放置任何的數據,因為這樣在后面的增刪查改的時候使用的時候會非常的方便
template<class T>
class _List
{typedef _ListNode<T> Node;public:typedef List_Iterator<T, T&, T*> Iterator;//const迭代器typedef List_Iterator<T, const T&, const T*> Const_Iterator;//這里為什么傳遞的是const T&和const T*呢,如果我們這樣傳遞的話,在實現iterator的時候,里面有個函數是//Ref operator*()//{//  return _it->_data;//}//這個時候因為我們傳遞的是const的,所以這個時候的Ref就成了const的了,這個時候的iterator所維護的節點//里面的數據就不可改變了,這就實現了const的作用了//這也是我們為什么設計iterator的時候為什么設計的是三個模板參數了//配合著const_iterator我們還需要模擬實現返回時const的end和begin_List():_head(new Node)   //這里報了一個錯誤就是,沒有合適的構造函數可以使用,原因是我上面寫_ListNode的時候//傳參了,但是這里沒有傳參數,所以我上面的額時候把傳遞的參數默認為d = 0這個時候就算是我不傳遞參數//也沒有什么問題了//  , _tail(_head){_head->_next = _head;_head->_prev = _head;}//這里開始設計的時候想著返回值是Node*,然后外面使用的時候,可以拿這個Node*構造一個迭代器,調用迭代器//的構造函數,就生成了一個迭代器了,但是問題在于,這樣做的話,是不是會引起我們的某些時候的一些誤解呢//我們可以直接返回的就是一個迭代器//但是如果我們這里設計的返回值是一個迭代器的話,我們需要在這個函數里面定義一個迭代器,然后返回的時候//生成一個臨時的對象,返回給外層的迭代器,這個時候調用了一次拷貝構造函數,返回給外層的迭代器的時候//又調用了一次拷貝構造函數,這樣會不會太復雜了呢//Iterator begin()//{//  if (_head->_next == _head)   //這里做了一個判斷就是當我們的鏈表是空的時候,就不能直接返回next//  {//      return Iterator(NULL);//  }//  return Iterator(_head->_next);//}//在使用Const_Iterator的時候出現了問題就是,為什么我下面的普通的begin函數不注釋 的時候,就編譯不過呢Const_Iterator begin() const   //返回值是Const_iterator為了和上面的匹配,然后后面 的一個const為為了//this指針所指向的內容{if (_head->_next == _head)   //這里做了一個判斷就是當我們的鏈表是空的時候,就不能直接返回next{return Const_Iterator(NULL);}return Const_Iterator(_head->_next);}//Node* begin() const//{//  if (_head->_next == _head)   //這里做了一個判斷就是當我們的鏈表是空的時候,就不能直接返回next//  {//      return NULL;//  }//  return _head->_next;//}/*Iterator end(){if (_head->_next == _head){return Iterator(NULL);}return Iterator(_head);}*/Const_Iterator end() const{if (_head->_next == _head){return Const_Iterator(NULL);}return Const_Iterator(_head);}void Pushback(const T& data){Node* cur = new Node(data);cur->_prev = _head->_prev;_head->_prev = cur;cur->_next = _head;cur->_prev->_next = cur;}//void Print()//{//  Const_Iterator it = begin();   //這個原因是,我們的begin不是 const的,但是我們的const_ietator是const的//  while (it != end())//  {//      cout << *it << endl;//      it++;//  }//}
private:Node* _head;
//  Node* _tail;
};void TestList()
{_List<int> l;l.Pushback(0);   //一開始的時候,我這里設置的這個參數是引用,這個時候是辯不過的,因為我們的0是在常量區的l.Pushback(1);l.Pushback(2);_List<int>::Const_Iterator it = l.begin();/*_List<int>::Ierator it = l.begin();cout << *it << endl;++it;cout << *it << endl;++it;cout << *it << endl;*/
//  _List<int>::Iterator it = l.begin();//*it = 5;//l.Print();/*list<int> l;l.push_back(0);l.push_back(1);list<int>::const_iterator it = l.begin();*/}

這里我們按照一個類一個類的來進行分析

_ListNode

首先看到的是_ListNode,這里就不用過多的解釋什么了,成員變量是兩個指向該節點的指針,和一個存放數據的東西;這里只實現了一個構造函數,構造函數完成的初始化的時候把兩個指針置為NULL

List_Iterator

  • 構造函數,我們的List_Iterator底層維護的實際上就是一個指針,指向的就是我們的鏈表的一個個節點,構造函數中是把一個指針給了迭代器中,這個 函數一般在我們的list中的begin函數中使用到
  • 拷貝構造函數。這個是拿一個已經存在的迭代器去初始化 另一個迭代器。
  • 前置 ++ 和后置 ++ 。Self& operator++()和Self operator++(int)
    這兩個函數是我們實現的重點,因為我們 開始寫迭代器 的目的就是希望能夠像一個指針使用它,直接使用 ++ 操作就可以實現對下一個節點的訪問了。這里我們 應該注意的一個問題就是,我們的前置 ++ 和后置 ++ 的返回值應該是不一樣的,因為前置++返回的是先 ++ 后返回,返回的是 ++ 后的結果,所以這個時候應該返回的是Self的引用,但是后置 ++ 是先返回在 ++ ,這個時候是不能夠使用引用的,因為我們既然要先返回再 ++ ,我們就要在 函數的里面先將 ++ 前的值記錄下來,所以這個時候就需要定義一個變量保存 ++ 前的結果,然后返回,如果這個時候使用的是引用的話,我們的函數結束之后,剛剛的變量就釋放掉了,所以這個時候的引用是錯誤的。

  • *號運算符的重載。這個重載返回的應該是迭代器 所指向的數據,所以這個時候返回的是T&,然后我們又在上面對這個T&進行了一個封裝,所以這個時候返回值直接使用Ref。

_List

關于鏈表,我們這里維護的是一個雙向的循環的有頭節點的雙向鏈表

typedef List_Iterator<T, T&, T*> Iterator;//const迭代器typedef List_Iterator<T, const T&, const T*> Const_Iterator;

這里重點說明一下,為什么要將迭代器聲明為三個模板參數的,這里就可以很好的體現了復用性了

這里為什么傳遞的是const T&和const T*呢,如果我們這樣傳遞的話,在實現iterator的時候,里面有個函數是
Ref operator*()
{
return _it->_data;
}
這個時候因為我們傳遞的是const的,所以這個時候的Ref就成了const的了,這個時候的iterator所維護的節點
里面的數據就不可改變了,這就實現了const的作用了
這也是我們為什么設計iterator的時候為什么設計的是三個模板參數了
配合著const_iterator我們還需要模擬實現返回時const的end和begin

總結

以上是生活随笔為你收集整理的STL模拟实现1.0 -- list和iterator模拟实现和简单分析的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。