C++標準庫中的list的實現原理
阿新 • • 發佈:2019-02-19
在C++中採用了大量的標誌模板庫(STL)實現程式的設計,這種設計方式使得不同型別的物件都能通用,而不再是C語言中的通常對於不同的型別需要重新設計或者或者比較採用間接的指標操作。C++中的這種方式簡化了寫程式碼的複雜度,但是增加了編譯器的複雜度和難度。
在資料結構中連結串列是比較基本的型別,在C++中連結串列是基於模板的類,因此在實際的使用過程中需要涉及到實際的型別。
當然在類中至少存在返回迭代器的begin()和end()函式,這兩個函式返回的迭代器分別指向連結串列的開始和連結串列結束的下一個地址,這是迭代器中經常容易理解錯誤的地方。 在C++中通常建立const_iterator類,然後iterator直接繼承const_iterator。 下面說說list類設計的基本思路: 首先、建立連結串列節點物件,實質上是完成對傳遞進來的型別的封裝操作,同時構成一個雙向連結串列的基本要素(prev、next指標)。節點肯定要存在建構函式,而且是直接初始化三個成員變數。 其次、建立迭代器類,實質上就是封裝一個節點指標,通過節點指標實現操作,至少要實現的操作符已說明。這兩個類都要設定List為友元類,因為這樣才能用List直接操作迭代器的相關操作。 最後、依靠上面的迭代器類和節點類,建立一個List類,該類中主要完成一些基本操作。其中需要注意的就是迭代器的操作,比如刪除元素和插入元素以後迭代器的變化問題等。 需要注意的是在List中採用了哨兵節點,這個哨兵節點並不算實際的操作物件,也就是為了保證肯定有目標所指向,存在一個head物件,這個物件的next就是實際的資料,而tail是迭代器所能到達的最後一個物件,但是這個物件並不是合理的區域,實際上end()實際上就是指向了tail節點,這兩個節點head和tail就是哨兵節點。具體的參看程式碼。 實現的基本形式如下:
)摺疊或開啟
點選(此處)摺疊或開啟
- #include<list>
- list<int> lint;
當然在類中至少存在返回迭代器的begin()和end()函式,這兩個函式返回的迭代器分別指向連結串列的開始和連結串列結束的下一個地址,這是迭代器中經常容易理解錯誤的地方。 在C++中通常建立const_iterator類,然後iterator直接繼承const_iterator。 下面說說list類設計的基本思路: 首先、建立連結串列節點物件,實質上是完成對傳遞進來的型別的封裝操作,同時構成一個雙向連結串列的基本要素(prev、next指標)。節點肯定要存在建構函式,而且是直接初始化三個成員變數。 其次、建立迭代器類,實質上就是封裝一個節點指標,通過節點指標實現操作,至少要實現的操作符已說明。這兩個類都要設定List為友元類,因為這樣才能用List直接操作迭代器的相關操作。 最後、依靠上面的迭代器類和節點類,建立一個List類,該類中主要完成一些基本操作。其中需要注意的就是迭代器的操作,比如刪除元素和插入元素以後迭代器的變化問題等。 需要注意的是在List中採用了哨兵節點,這個哨兵節點並不算實際的操作物件,也就是為了保證肯定有目標所指向,存在一個head物件,這個物件的next就是實際的資料,而tail是迭代器所能到達的最後一個物件,但是這個物件並不是合理的區域,實際上end()實際上就是指向了tail節點,這兩個節點head和tail就是哨兵節點。具體的參看程式碼。 實現的基本形式如下:
點選(此處
- #ifndef __MYLIST_H_H_
- #define __MYLIST_H_H_
- #include<iostream>
- namespace myspace
- {
- templateObject>
- class List
- {
- private:
- /*封裝物件,形成連結串列節點*/
- struct Node
- {
- Object data;
- struct Node *
- struct Node *next;
- /*節點建構函式*/
- Node(const Object
&d = Object(), Node
*p =
NULL, Node
*n = NULL)
- :data(d), prev(p),
next(n){}
- };
- public:
- /*建立一個常量迭代器類,這是容器設計的關鍵*/
- class
- {
- public:
- const_iterator():current(NULL)
- {}
- /*過載迭代器的值*/
- const Object
& operator*()const
- {
- return retrieve();
- }
- /*過載前向++操作符*/
- const_iterator & operator++
()
- {
- current = current->next;
- return *this;
- }
- /*過載後向++操作符,因為是一個區域性物件不能返回引用*/
- const_iterator operator++(int)
- {
- const_iterator old
= *this;
- ++(*this);
- return old;
- }
- /*判斷迭代器是否相同,實質上就是判斷指向的節點是否相同*/
- bool operator==(const const_iterator
&rhs)
const
- {
- return current == rhs.current;
- }
- /*呼叫==操作符*/
- bool operator!=(const const_iterator
&rhs)const
- {
- return (!(*this
== rhs));
- }
- protected:
- /*迭代器實質就是一個節點指標*/
- Node *current;
- /*獲得連結串列中的內容*/
- Object & retrieve()
const
- {
- return current->data;
- }
- /*基於指標引數的迭代器建構函式,保證只有List使用*/
- const_iterator(Node
*p):current
(p)
- {}
- /*友元類,可以呼叫迭代器的私有成員*/
- friend class List<Object>;
- };
- /*一般迭代器,直接繼承const_iterator*/
- class iterator
: public const_iterator
- {
- public:
- iterator():const_iterator()
- {}
- /*得到物件的值*/
- Object &operator*()
- {
- return retrieve();
- }
- /*基於const的過載*/
- const Object& operator*()const
- {
- return const_iterator::operator*();
- }
- /*前向++操作符*/
- iterator &operator++()
- {
- current = current->next;
- return *this;
- }
- /*後向++操作符*/
- iterator operator++(int)
- {
- iterator *old
= *this;
- ++(*this);
- return old;
- }
- protected:
- /*基於節點的迭代器建構函式*/
- iterator(Node
*p):const_iterator(p)
- {}
- friend class List<Object>;
- };
- public:
- List()
- {
- init();
- }
- ~List()
- {
- clear();
- delete head;
- delete tail;
- }
- List(const List
&rhs)
- {
- /*建立哨兵節點*/
- init();
- /*複製資料*/
- *this
= rhs;
- }
- const List
& operator=(const List
&rhs)
- {
- if(this
==
&rhs)
- return *this;
- /*清除原有的資訊*/
- clear();
- /*新增新的物件*/
- for(const_iterator itr
= rhs.begin(); itr
!= rhs.end();
++ itr)
- push_back(*itr);
- return *this;
- }
- /*得到迭代器,實質上就是得到節點指標*/
- iterator begin()
- {
- /*iterator()是建構函式*/
- return iterator(head->next);
- }
- const_iterator begin()const
- {
- return const_iterator(head->next);
- }
- iterator end()
- {
- return iterator(tail);
- }
- const_iterator end()const
- {
- return const_iterator(tail);
- }
- int size()const
- {
- return theSize;
- }
- bool empty()const
- {
- return size()
== 0;
- }
- void clear()
- {
- while(
!empty())
- pop_front();
- }
- /*得到第一個元素*/
- Object & front()
- {
- /*採用了迭代器begin()*/
- return *begin();
- }
- const Object
&front()const
- {
- return *begin();
- }
- Object &back()
- {
- /*end()指向最後一個物件的下一個地址,因此需要--*/
- return *--end();
- }
- const Object
&back()const
- {
- return *--end();
- }
- /***********************************************
- *從頭插入新的節點,這時候的begin已經不再是begin
- *因此插入操作會導致迭代器出錯
- ***********************************************/
- void push_front(const Object
&x)
- {
- insert(begin(), x);
- }
- /*從後插入新的節點,這時候會將end後移*/
- void push_back(const Object
&x)
- {
- insert(end(), x);
- }
- /*從頭彈出一個物件*/
- void pop_front()
- {
- erase(begin());
- }
- void pop_back()
- {
- erase(--end());
- }
- /*插入物件,引數是迭代器和資料*/
- iterator insert(iterator itr,
const Object &x)
- {
- /*得到當前迭代器的指標*/
- Node *p
= itr.current;
- theSize ++;
- /*
- *Node
*np = Node(x,p->prev,p);
- this means that np->prev
= p->prev,
- and np->next
= p;
- update the p->prev
and p->prev->next;
- *p->prev->next
= np;
- *p->prev
= np;
- */
- return iterator(p->prev=p->prev->next=
new Node(x,p->prev, p));
- }
- /*刪除迭代器處的物件,因此刪除也會導致迭代器破壞*/
- iterator erase(iterator itr)
- {
- /*得到當前迭代器的指標*/
- Node *p
= itr.current;
- /*得到新的迭代器,並初始化*/
- iterator retVal(p->next);
- /*更新連結串列的連結關係*/
- p->prev->next
= p->next;
- p->next->prev
= p->prev;
- /*刪除物件*/
- delete p;
- /*使得物件數減少*/
- theSize --;
- /*返回新的迭代器*/
- return retVal;
- }
- /*刪除迭代器指向的物件*/
- iterator erase(iterator start, iterator
end)
- {
- /*for中不使用++itr的原因是erase之後
- *就是下一個迭代器,因此不需要++操作*/
- for(iterator itr
= start; itr
!=
end; )
- {
- /*該操作會導致迭代器更新到下一個*/
- itr =
erase(itr);
- }
- return itr;
- }
- private:
- /*連結串列中的資料成員*/
- int theSize;
- Node *head;
- Node *tail;
- /*初始化函式*/
- void init()
- {
- theSize = 0;
- /*create two sentinel node*/
- /*構建兩個哨兵節點,也就是兩個並不算在結構體中的物件*/
- head = new Node;
- tail = new Node;
- /*繫結起來*/
- head->next
= tail;
- tail->prev
= head;
- }
- };
- }
- #endif