2012-02-11 61 views
2

我嘗試下面的代碼擦除外國迭代

list<int> a ={1,2}; 
list<int> b ={3,4}; 
a.erase(b.begin()); 
for (auto x:b) cout << x << endl; 

奇怪的是,該方案沒有任何錯誤運行良好。它打印出來的是4.我想知道爲什麼擦除是一個成員函數時,該對象已經隱式在迭代器中。

+0

@zerOne:查看我更新的答案。 – Nawaz 2012-02-12 03:43:04

+0

謝謝。當我得到更多積分時,我會回來投票。 – zer0ne 2012-02-13 23:15:12

回答

3

這調用不確定的行爲,因爲你傳遞從一個容器取得迭代其他容器的功能。 ab是兩個不同的容器,它們不一樣。

未定義的行爲意味着任何事情都可能發生:它可能按預期運行,或者它可能不會。語言規範和編譯器都沒有保證它會起作用。恰當地說「未定義的行爲」。

你應該做的是這樣的:

auto value = *(b.begin()); //value is int 
auto it = std::find(a.begin(), a.end(), value); //returns iterator 
if (it != a.end()) 
    a.erase(it); //well-defined, as the iterator belongs to the same container! 

或者,如果你想刪除等於value所有元素,那麼你可以簡單地這樣做:

a.remove(value); //std::list has remove member function 

然而,如果你使用std::vector,你應該在大多數情況下使用它。這是C++默認容器類型,只有在您充分的理由這樣做,你應該使用std::list

std::vector<int> a ={1,2}; 
std::vector<int> b ={3,4}; 

//if you want to remove one element: 
auto value = *(b.begin()); //value is int 
auto it = std::find(a.begin(), a.end(), value); //returns iterator 
if (it != a.end()) 
    a.erase(it); //well-defined, as the iterator belongs to the same container! 

如果你想刪除等於value所有元素,那麼你可以申請受歡迎Erase-Remove Idiom爲:

a.erase(std::remove(a.begin(), a.end(), value), a.end()); 

注意std::vector沒有remove()成員函數,這就是爲什麼你申請這個成語。你可以閱讀my answer here,更詳細地討論這個問題。

+0

那麼如何測試迭代器是否在容器的範圍內。 在這個例子中,b.begin()!= a.end()並不意味着b.begin()是一個有效的迭代器。 – zer0ne 2012-02-11 17:29:51

+0

@ zer0ne:我想,你無法知道(可靠地)。你需要以這樣的方式編程,以至於你永遠不會需要它。 – Nawaz 2012-02-11 17:31:40

1

這簡直是不確定的行爲,所以任何事情都有可能發生,任何你觀察的是在某些方面的「預期行爲」。

不要這樣做。

std::list::erase的先決條件很明顯,參數是容器的元素的迭代器

0

C++是一個標準,它不需要迭代器知道它屬於哪個容器。我們不能僅僅因爲在一個特定的實現中函數可以在不需要特定參數的情況下完成它的工作而改變標準。

0

其他人提到,根據C++標準,這會導致未定義的行爲。

然而,雙鏈表的美妙之處在於,從列表中移除節點只需要指向該節點的指針,不需要引用容器本身,例如,:

template<class Tag> 
inline void unlink(ListNode<Tag>* node) 
{ 
    ListNode<Tag> *prev = node->prev_, *next = node->next_; 
    prev->next_ = next; 
    next->prev_ = prev; 
    node->prev_ = node; 
    node->next_ = node; 
} 

你的代碼恰好能夠正常工作,因爲std::list<>通常爲雙鏈表實現,list<>::erase()從迭代器獲得的指針鏈表節點並執行類似上面的代碼。如果您爲迭代器啓用調試支持,則此代碼可能會導致運行時斷言。

0

這只是一個實施細節,如果std::list::erase實際上並不需要知道this。它是與其他容器保持一致性的成員函數。實際上,您可以將容器作爲模板參數,並致電container.erase(iter);

相關問題