2017-05-05 59 views
2

我有這樣的代碼:循環中的C++映射擦除元素是否使迭代器無效? (C++ 03)

#include <cstdlib> 
#include <map> 
#include <string> 
#include <iostream> 
#include <algorithm> 
#include <stdint.h> 

class Table 
{ 
public: 
    std::map<uint32_t, std::string> m_map; 

    typedef std::map<uint32_t, std::string>::iterator Iterator_t; 
    typedef std::map<uint32_t, std::string>::const_iterator ConstIterator_t; 

    Table() : m_map() { } 

    void 
    Erase (Iterator_t entry_it, const std::string & reason) 
    { 
    std::cout << reason << " " << entry_it->first << ":" << entry_it->second << "\n"; 
    m_map.erase (entry_it); 
//  m_map.erase (entry_it->first); 
    } 

    bool 
    Insert (const uint32_t & key, const std::string & value) 
    { 
    ConstIterator_t found_it = m_map.find (key); 

    if (found_it != m_map.end()) 
     { 
     std::cout << "Key [" << key << "] already exists\n"; 
     return false; 
     } 

    m_map.insert (std::make_pair (key, value)); 
    std::cout << key << ":" << value << " inserted\n"; 
    return true; 
    } 

    void 
    Print() const 
    { 
    std::cout << "Table: "; 
    for (ConstIterator_t it = m_map.begin(); it != m_map.end(); ++it) 
     { 
     std::cout << it->first << ":" << it->second << " "; 
     } 
    std::cout << "\n"; 
    } 

    void 
    Purge() 
    { 
    for (Iterator_t it = m_map.begin(); it != m_map.end(); ++it) 
     { 
     if (it->second.length() <= 4) 
      { 
      Erase (it, "Erase entry "); 
      } 
     } 
    } 
}; 

int 
main (int argc, char** argv) 
{ 
    Table table; 

    table.Insert (1, "one"); 
    table.Insert (2, "two"); 
    table.Insert (3, "three"); 
    table.Insert (4, "four"); 
    table.Insert (5, "nine"); 
    table.Insert (6, "six"); 
    table.Insert (7, "seven"); 
    table.Insert (8, "eight"); 
    table.Insert (9, "nine"); 
    table.Print(); 
    std::cout << "\n"; 

    table.Purge(); 
    table.Print(); 
    std::cout << "\n"; 

    return 0; 
} 

產生以下的輸出:

1:one inserted 
2:two inserted 
3:three inserted 
4:four inserted 
5:five inserted 
6:six inserted 
7:seven inserted 
8:eight inserted 
9:nine inserted 
Table: 1:one 2:two 3:three 4:four 5:nine 6:six 7:seven 8:eight 9:nine 

Erase entry 1:one 
Erase entry 2:two 
Erase entry 4:four 
Erase entry 6:six 
Erase entry 9:nine 
Table: 3:three 5:five 7:seven 8:eight 

正如你可以看到5:five應該已被刪除,但事實並非如此。我不完全明白爲什麼。

m_map.erase()的兩個重載我試過產生相同的結果。我找不到地圖的std::remove_if版本。

我必須指出,這個必須在C++ 03,我發現只有C++ 11/14的答案。你能告訴我如何解決這個問題嗎?

回答

2

As @sam回答說,std::map::erase會使迭代器無效到擦除元素。之後,無效迭代器用於for循環中的增量操作,這是未定義的行爲。

擦除元素的引用和迭代器將失效。

爲了解決這個問題,從C++ 11開始,您可以使用返回值erase,它是被移除元素後面的迭代器。但對於C++ 03,您必須手動執行此操作。例如

class Table 
{ 
public: 
    ...  
    void 
    Purge() 
    { 
    for (Iterator_t it = m_map.begin(); it != m_map.end();) 
     { 
     if (it->second.length() <= 4) 
      { 
      Erase (it++, "Erase entry "); 
      } 
     else 
      { 
      ++it; 
      } 
     } 
    } 
}; 

上述代碼的一點是,erase發生之前執行增量(而it仍然是一個有效的迭代)。我們利用it++將返回原始值it這一事實。因此評估順序將爲:(1)it遞增; (2)原始值it傳遞給erase; (3)元素是erase d。

+1

'std :: map :: erase'不會在C++ 03中返回迭代器:http://en.cppreference.com/w/cpp/container/map/erase – NathanOliver

+0

@NathanOliver不夠公平。回答修改。 – songyuanyao

+0

謝謝你的快速解答。 如果我正確理解了新代碼,'Erase(it ++,「Erase entry」);'發送迭代器'it'的當前值,然後增加它(移到下一個條目),所以它指向一個有效即使先前的輸入已被刪除,也可以輸入地圖。我對麼? –

4

是的,這是未定義的行爲。

erase()使迭代器無效到已移除的映射值。循環然後嘗試增加失效的迭代器。如果在循環內直接調用erase()或從循環調用的函數中,這並不重要。無論哪種方式,這都是未定義的行爲。對於C++ 03,C++ 11和C++ 14都是如此。從根本上講,這是地圖的工作原理。