2017-05-05 57 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都是如此。从根本上讲,这是地图的工作原理。