2014-03-24 47 views
0

我有一個關於C++列表的問題。下面是我不太明白的代碼。C++列表<T>如何處理插入元素?

#include <iostream> 
#include <list> 
#include <string> 
using namespace std; 

struct Customer { 
string firstName; 
string lastName; 
}; 

void replace(list<Customer>& customers, int index, Customer item) 
{ 
std::list<Customer>::iterator it = customers.begin(); 
advance(it, index); 
*it = item; 
} 

Customer get (list<Customer>& customers, int index) 
{ 
std::list<Customer>::iterator it = customers.begin(); 
advance(it, index); 
return *it; 
} 

int main() { 

list<Customer> customers; 
Customer c1; 
c1.firstName = "Jack"; 
c1.lastName = "Smith"; 
Customer c2; 
c2.firstName = "Jane"; 
c2.lastName = "Doe"; 

insert(customers, 50, c1); 

cout << get(customers,0).firstName << endl; //outputs Jack even though I inserted it at index 50 

insert(customers, 49, c2); 

cout << get(customers,0).firstName << endl; //outputs Jane 

cout << get(customers,50).firstName << endl; //where did Jack go? 

return 0; 
} 

我正在使用Eclipse C++和gcc。所以我的問題是:

1 - 爲什麼這個例子不會崩潰,因爲我直接在索引50插入項目?

2 - 索引0 - 49處的點發生了什麼?

3 - 在insert()和get()方法中,檢查列表大小的索引是否正確並確保它們不通過列表邊界?

+0

未定義的行爲,在超出'end()'前進的迭代器的位置。這意味着該方案可以做任何事情 - 包括不會崩潰,並做你所看到的。 –

+1

此外,您的插入不插入,但在給定的位置替換項目。 – hivert

+0

將方法名稱從insert()更改爲replace(),錯過了該方法。所以正確的做法是手動檢查傳入的索引是否在預期範圍內? –

回答

2

爲什麼這個例子不會崩潰,因爲我直接在索引50插入項目?

因爲未定義的行爲不一定會導致崩潰。

你沒有在索引50處插入。你正在使用一個末端迭代器(它不能被增加),然後嘗試增加它50次;然後通過解引用最終無效的迭代器來進一步跳躍到未定義的行爲。

GNU庫實現list作爲循環列表,其中虛擬節點充當「過去結束」位置。因此,在這個實現中,遞增迭代迭代器將返回到列表的開始處,並且取消引用它將導致可訪問的內存,所以不會發生崩潰。您只需寫入該虛擬元素,然後覆蓋它。

當您執行無效操作時,其他實現的行爲可能會有所不同。

在索引0 - 49處發生了什麼?

它們不存在,因爲您從不在列表中放入任何東西。

in insert()和get()方法是否正確檢查列表大小的索引並確保它們不通過列表邊界?

是的。 advance將不檢查序列的結尾,因爲它無法知道那是什麼。如果你試圖超越最後,你會得到不確定的行爲,所以最好先檢查。

+0

謝謝你的回答和解釋。 –

0

您的清單有0個要素。嘗試撥打customers.size()

所以std::advance返回customers.end(),這是無效的迭代器。通過取消引用,您將獲得例外或UB,具體取決於您的構建選項。