程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> [STL] list的使用

[STL] list的使用

編輯:關於C++

1. list功能
list是雙向循環鏈表,每一個元素都知道前面一個元素和後面一個元素.list對象自身提供了兩個pointer用來指向第一個和最後一個元素.每個元素都有pointer指向前一個和後一個元素.如果想要插入新元素,只需要操縱對應的pointer即可.因此list在幾個方面與array,vector不同:
- list不支持隨機訪問,如果你要訪問第5個元素,就得順著串鏈逐一爬過前4個元素.所以在list中隨機訪問一個元素是很緩慢的,然而你可以從兩端開始航行整個list,所以訪問第一個或最後一個元素速度很快.
- 任何位置上(不只兩端)執行元素的插入或刪除都非常快,因為無需移動任何其他元素.實際上內部只是進行了一些pointer操作而已.
- 插入和刪除動作並不會造成其他元素的各個pointer,reference和iterator失效.
- list對於異常的處理方式是:要麼操作成功,要麼什麼都不發生,絕對不會陷入”只成功一半”這種進退維谷的境地.

list所提供的成員函數反映出它與array,vector不同:
- list提供front(),push_front()和pop_front(),back(),push_back()和pop_back()等操作函數.
- 由於不支持隨機訪問,list既不提供下表操作符,也不提供at().
- list並未提供容量,空間重新分配等操作函數,因為完全無必要.每個元素都有自己的內存,在這個元素刪除前一直有效.

2.list操作
在使用list前必須包括頭文件#include
本文例子中到的兩個list對象c1,c2分別為c1(10,20,30) c2(40,50,60)。還有一個迭代器it = list::iterator用來指向c1或c2元素。

1)、list的構造函數和析構函數

listc;             // 構造函數,產生一個空list,沒有任何元素
listc(c2);         // copy 構造函數,建立c2的同類型list,並成為c2的一份拷貝
listc = c2;        // copy 構造函數,建立一個新的list作為c2的拷貝
listc(rv);         // move 構造函數,建立一個新的list,取rvalue rv的內容(始自C++11)
listc = c2;        // move 構造函數,建立一個新的list,取rvalue rv的內容(始自C++11)
listc(initlist);   // 建立一個list,以初值列initlist的元素為初值(始自C++11)
listc = initlist;  // 建立一個list,以初值列initlist的元素為初值(始自C++11)    
listc(n);          // 利用元素的default構造函數生成一個大小為n的list
listc(n,elem);     // 建一個含n個元素的list,值都是elem
listc(c1.begin(),c1.end());  // c含c1一個區域的元素[First, _Last)。
c.~list();             // 銷毀所有元素,釋放內存

2)、list的非易型操作

c.empty()    // 返回容器是否為空(相當於size()==0但也許較快)
c.size()     // 返回目前的元素個數
c.max_size() // 返回元素個數之最大可能量
c1 == c2     // 返回c1是否等於c2(對每個元素調用==)
c1 != c2     // 返回c1是否不等於c2(相當於!(c1 == c2))
c1 > c2      // 返回c1是否大於c2
c1 >= c2     // 返回c1是否大於等於c2
c1 < c2      // 返回c1是否小與c2
c1 <= c2     // 返回c1是否小與等於c2

3)、assign() 給list賦值:

c.assign(n,elem);   // 復制n個elem,賦值給c
c.assign(beg,end);  // 將區間[beg,end)內的元素賦值給c
c.assign(initlist); // 將初值列initlist所有元素賦值給c

4)、swap() 交換兩個list

c1.swap(c2);  // 置換c1和c2的數據
swap(c1,c2);  // 置換c1和c2的數據

5)、list元素直接訪問

front() 返回第一個元素  (不檢查是否存在第一個元素)
int i=c1.front(); // i=10

back() 返回最後一個元素 (不檢查是否存在最後一個元素)
int i=c1.back();  // i=30

6)、迭代器相關函數

begin() 返回一個迭代器,指向第一個元素
it=c1.begin();    // *it=10

end() 返回一個迭代器,指向最後一個元素的下一位置
it=c1.end(); //*(--it)=30;

rbegin() 返回一個反向迭代器, 指向反向迭代器第一個元素 
list::reverse_iterator riter=c1.rbegin(); // *riter=30

rend() 返回一個反向迭代器, 指向反向迭代器最後一個元素的下一個位置 
list::reverse_iterator riter=c1.rend(); // *(--riter)=10

cbegin() 返回一個const迭代器, 指向第一個元素 (始自C++11)
list::const_iterator citer=c1.cbegin(); // *citer=10且為const

cend() 返回一個const迭代器, 指向最後一個元素的下一個位置 (始自C++11)
list::const_iterator citer=c1.cend(); // *(--citer)=30且為const

crbegin() 返回一個const反向迭代器, 指向反向迭代器第一個元素 (始自C++11)
list::const_reverse_iterator criter=c1.crbegin(); // *criter=30且為const

crend() 返回一個const反向迭代器, 指向反向迭代器最後一個元素的下一個位置 (始自C++11)
list::const_reverse_iterator criter=c1.crend(); // *(--criter)=10且為const

7)、clear() 刪除所有元素

c1.clear();   //c1為空  c1.size為0;

8)、erase() 刪除一個元素 , 注意事項參照下面”注意事項1”

c1.erase(pos);     // 移除pos位置上的元素,返回下一個元素的位置
c1.erase(beg,end); // 移除區間[beg,end)內所有元素,返回下一個元素的位置

9)、remove() 從list刪除元素

c.remove(val);     // 移除所有其值為val的元素

10)、remove_if() 按指定條件刪除元素

c1.remove_if(op); // 移除所有"造成op(elem)結果為true"的元素

11)、resize() 改變list的大小

c1.resize(num)      // 將元素數量改為num(如果size()變大,多出來的新元素都需以default構造函數完成初始化)
c1.resize(num,elem) // 將元素數量改為num(如果size()變大,多出來的新元素都是elem的拷貝)

12)、insert() 插入一個元素到list中

c.insert(pos,elem);     // 在iterator位置pos之前插入一個elem拷貝,並返回新元素的位置
c1.insert(pos,n,elem);  // 在iterator位置pos之前插入n個elem拷貝,並返回第一個新元素的位置(或返回pos--如果沒有新元素的話)
c1.insert(pos,beg,end); // 在iterator位置pos之前插入區間[beg,end)內所有元素的一份拷貝,並返回第一個新元素的位置(或返回pos--如果沒有新元素的話)
c1.insert(pos,initlist);// 在iterator位置pos之前插入初值列initlist內所有元素的一份拷貝,並返回第一個新元素的位置(或返回pos--如果沒有新元素的話;始自C++11)

例如:
c1.insert(++c1.begin(),100);   // c1(10,100,20,30)
c1.insert(c1.begin(),2,200);   // c1(200,200,20,30);
c1.insert(++c1.begin(),c2.begin(),--c2.end()); // c1(10,40,50,20,30);
c1.insert(++c1.begin(),{100,200});   // c1(10,100,200,20,30)

13)、emplace() 插入以args為初值的元素

c.emplace(pos,args...)  // 在iterator位置pos之前插入一個以args為初值的元素,並返回新元素位置(始自C++11)
c.emplace_back(args...) // 在末尾插入一個以args為初值的元素,不返回任何東西(始自C++11)
c.emplace_front(args...) // 在起點插入一個以args為初值的元素,不返回任何東西(始自C++11)

14)、pop_back() 刪除最後一個元素 ,但是不返回它

c1.pop_back();  // c1(10,20);

15)、pop_front() 刪除第一個元素 ,但是不返回

c1.pop_front(); // c1(20,30)

16)、push_back() 在list的末尾添加一個元素

c1.push_back(100); // c1(10,20,30,100)

17)、push_front() 在list的頭部添加一個元素

c1.push_front(100); // c1(100,10,20,30)

18)、merge() 合並兩個list 並使之默認升序(也可改)

c.merge(c2)    // 假設c和c2容器都包含op()准則下的已排序元素,將c2中的全部元素轉移到c,並保證合並後的list仍已排序
c.merge(c2,op) // 假設c和c2容器都包含已排序元素,將c2中的全部元素轉移到c,並保證合並後的list在op()准則下已排序

例如:
c2.merge(c1);   //c1現為空;c2現為c2(10,20,30,40,50,60)
c2.merge(c1,greater()); //同上,但c2現為降序

19)、reverse() 把list的元素倒轉

c1.reverse(); // c1(30,20,10)

20)、sort() 給list排序, 默認升序(可自定義)

c.sort() // 以operator<為准則對所有元素排序
c.sort(op) // 以op()為准則對所有元素排序

例如:
c1.sort();  // c1(10,20,30)
c2.sort(greater()); //同上,但c1現為降序

21)、splice() 合並兩個list

c.splice(pos,c2) // 將c2內的所有元素轉移到c之內,迭代器pos之前
c.splice(pos,c2,c2pos) // 將c2內的c2pos所指元素轉移到c內pos之前
c.splice(pos,c2,c2beg,c2end) // 將c2內的[c2beg,c2end)區間內所有元素轉移到c內pos之前

例如:
c1.splice(++c1.begin(),c2); //c1(10,40,50,60,20,30) c2為空 全合並
c1.splice(++c1.begin(),c2,++c2.begin()); //c1(10,50,20,30) ; c2(40,60) 指定元素合並
c1.splice(++c1.begin(),c2,++c2.begin(),c2.end()); //c1(10,50,60,20,30); c2(40) 指定范圍合並

22)、unique() 刪除list中重復的元素

c.unique() // 如果存在若干相鄰而數值相同的元素,就移除重復元素,只留一個
c.unique(op) // 如果存在若干相鄰元素都使op()的結果為true,則移除重復元素,只留一個

例如:
c1.unique(); // 假設c1開始為(-10,10,10,20,20,-10),則之後為c1(-10,10,20,-10)

以上就是list的基本用法,實際使用中還需要注意一下幾點,後續遇到再加補充.

注意事項:erase使用注意 參考:http://blog.sina.com.cn/s/blog_66f74d9f0100om0f.html
常用的刪除容器中元素的方法是如下(方法1):

list< int> List;
list< int>::iterator iter;
for( iter = List.begin(); iter != List.end(); )
{
if(1)
{
iter = List.erase( iter );
}
else
{
iter++;
}
}

也可以這樣寫(方法2):
list< int> List;
list< int>::iterator iter;
for( iter = List.begin(); iter != List.end(); )
{
if(1)
{
List.erase( iter++ );
}
else
{
iter++;
}
}

有一種錯誤的寫法(注意同方法2比較)
list< int> List;
list< int>::iterator iter;
for( iter = List.begin(); iter != List.end(); )
{
if(1)
{
List.erase( iter );
}

    iter++;
}

我們看一下erase()函數的源代碼(僅列出release下的代碼)。
iterator erase(iterator _Where)
{ // erase element at _Where
_Nodeptr _Pnode = (_Where++)._Mynode();

if (_Pnode != _Myhead)
    {    // not list head, safe to erase
    _Nextnode(_Prevnode(_Pnode)) = _Nextnode(_Pnode);
    _Prevnode(_Nextnode(_Pnode)) = _Prevnode(_Pnode);
    this->_Alnod.destroy(_Pnode);
    this->_Alnod.deallocate(_Pnode, 1);
    --_Mysize;
    }
return (_Where);
}
函數在返回的時候,是返回當前迭代器的下一個節點。所以當 iter = List.erase( iter ); 執行以後,迭代器自動指向了下一個元素。而對於入參中的iter,所指的地址已經被銷毀,所以寫的時候,應該注意加上前面的iter =
那另外的一種寫法,List.erase( iter++ ); 為什麼也是對的呢?
研究了一下,這裡需要講一下++運算符的操作.
_Myt_iter& operator++()
{ // preincrement
++((_Mybase_iter )this);
return (*this);
}

    _Myt_iter operator++(int) 
        {    // postincrement
        _Myt_iter _Tmp = *this;
        ++*this;
        return (_Tmp);
        }

++實際上可以看做是一個函數。
對於++在後的情況(例如i++),函數在運行的時候,將運算的數據i已經改變,但是函數的返回值是操作之前的數據,所以在我們看來,i++好像是先進行了i的讀取,才+1。

回到迭代器,List.erase( iter++ );就沒有問題了。

對於那種錯誤的方法,List.erase( iter );在執行以後,iter所指的對象已經被銷毀,所以再對iter進行操作是非法的,程序會出錯。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved