2015-04-01 121 views
0

我正在編程一個多線程的c + + WIN套接字服務器,我遇到了一些奇怪的問題。向量迭代和刪除

我正在使用矢量來存儲活動連接。我用win mutex鎖定向量,然後嘗試遍歷它以查找所有關閉的連接並刪除它們,然後釋放互斥鎖。

代碼:

if (!m_activeConnections.empty()){ 
    for(std::vector<Connection*>::iterator it = m_activeConnections.begin(); it != m_activeConnections.end(); ++it) { 
     if ((*it)->isClosed()){ 
      delete *it; 
      it = m_activeConnections.erase(it); 
      break; 
     } 
    } 
    cout << "\n \t Active Connections: " << m_activeConnections.size() << endl; 
} 

它是這樣工作的,但是當我刪除break線總是去與迭代it指向一個更在循環和值爲0xAAAAAA拋出異常。如果這個刪除是在創建新連接的同一個線程中完成的,即使沒有中斷,它也可以正常工作。爲什麼是這樣?

回答

1

無論何時您修改範圍,您都必須確保更新了用於遍歷範圍的迭代器,這種方式與從範圍中刪除時迭代器失效的方式相兼容。

將此方法應用於矢量的一個簡單示例是以下循環。請注意,從擦除點開始,擦除將使所有迭代器無效,因此當擦除時需要獲取新的迭代器,並且每次都需要重新計算end()(即,不要將末端計算提升到循環外):

for (auto it = m_activeConnections.begin(); it != m_activeConnections.end();) 
{ 
    if ((*it)->isClosed()) { it = m_activeConnections.erase(it); } 
    else     { ++it;        } 
} 

從矢量中擦除的更好方法是將要擦除的元素移動到矢量的後面,然後一次擦除整個範圍,並避免始終移動尾部範圍。一般來說,我們做這個有remove_if,但你需要添加一點取巧的同時刪除你的情況指針對象:

m_activeConnections.erase(
    std::remove_if(m_activeConnections.begin(), 
        m_activeConnections.end(), 
        [](Connection * p) { 
         if (p->isClosed()) { delete p; return true; } 
         return false;}), 
    m_activeConnections.end()); 

,如果你改變了你的容器std::vector<std::unique_ptr<Connection>>你可能避免掛羊頭賣狗肉:讓每個負責一個班(矢量包含,唯一指針刪除),算法變得可組合。第一個分區的範圍內根據需要刪除,然後刪除,然後刪除範圍:

如果你不能讓你的代碼簡單的通過選擇適當的抽象,你也可以嘗試更復雜的算法

auto it = std::stable_partition(m_activeConnections.begin(), 
           m_activeConnections.end(), 
           [](Connection * p) { return p->isClosed(); }); 

for (auto kt = it; kt != m_activeConnections.end(); ++kt) 
{ 
    delete *kt; 
} 

m_activeConnections.erase(it, m_activeConnections.end()); 
+0

哦,謝謝你,我是一個C++初學者。我習慣於Java,並沒有注意到這一點。謝謝。 – jack 2015-04-02 00:00:50

+1

實際上,如果目標是同時刪除該項目並刪除該項目,那麼'remove_if'將無法在這裏工作。如果要使用算法方法,則首先使用'std :: stable_parttion',然後'刪除'並擦除分區「壞」一側的項目。 – PaulMcKenzie 2015-04-02 00:35:53

+0

@PaulMcKenzie:好點,我更新了答案! – 2015-04-02 07:43:43