2009-12-25 38 views
0

我想了解一个代码,这里的片段,这是造成困惑:一元指针递增函数调用VS增量前/后函数调用

typedef map<int, Person, less<int> > people_map; 
people_map people; 
. 
. 
. 
cout << "Erasing people of age 100" << endl; 

    for (people_map::iterator j = people.begin(); j != people.end();) { 
     if (j->second.GetAge() == 100) 
     { 

      people.erase(j++); // iterator is advanced before the erase occurs 

     } 
     else 
      ++j; // advance the iterator 
    } // end of erase loop 

的困惑是:如果我想以后递增Ĵ该函数调用它会导致分段错误。我无法理解为什么:

我改变它的东西是这样的:

if (j->second.GetAge() == 100) 
     { 
      temp = j++; 
      j--; 
      people.erase(j); // iterator is advanced before the erase occurs 
      j=temp; 

     } 

原因分段错误。

或这样的:

if (j->second.GetAge() == 100) 
     { 
      people.erase(j); // iterator is advanced before the erase occurs 
      j++; 
     } 

使段错误。

下面是完整的程序清单:

// disable warnings about long names 
#ifdef WIN32 
#pragma warning(disable : 4786) 
#endif 

#include <string> 
#include <map> 
#include <algorithm> 
#include <iostream> 
#include <sstream> 
#include <iterator> 
#include <functional> 

using namespace std; 

class Person { 
    // private members 
    string m_sName; 
    string m_sEmail; 
    int m_iAge; 

public: 

    // constructor 

    Person(const string sName, 
      const string sEmail, 
      const int iAge) : 
    m_sName(sName), m_sEmail(sEmail), m_iAge(iAge) { 
    }; 

    // default constructor 

    Person() : m_iAge(0) { 
    }; 

    // copy constructor 

    Person(const Person & p) : 
    m_sName(p.m_sName), m_sEmail(p.m_sEmail), m_iAge(p.m_iAge) { 
    }; 

    // operator = 

    Person & operator=(const Person & rhs) { 
     // don't assign to self 
     if (this == &rhs) 
      return *this; 

     m_sName = rhs.m_sName; 
     m_sEmail = rhs.m_sEmail; 
     m_iAge = rhs.m_iAge; 
     return *this; 
    }; 

    // access private members 

    string GetName() const { 
     return m_sName; 
    }; 

    string GetEmail() const { 
     return m_sEmail; 
    }; 

    int GetAge() const { 
     return m_iAge; 
    }; 

}; // end of class Person 

// function object to print one person 

class fPrint { 
    ostream & m_os; 

public: 

    // constructor - remember which stream to use 

    fPrint(ostream & os) : m_os(os) { 
    }; 

    // person object arrives as a pair of key,object 

    void operator() (const pair <const int, const Person> & item) const { 
     m_os << "# " << item.first << " - name: " 
       << item.second.GetName() 
       << " - " << item.second.GetEmail() 
       << ", age " << item.second.GetAge() 
       << endl; 
    }; 

}; // end of class fPrint 

// declare type for storing people (numeric key, person object) 
typedef map<int, Person, less<int> > people_map; 

int main(void) { 
    // make a map of people 
    people_map people; 

    // add items to list 
    people [1234] = Person("Nick", "[email protected]", 15); 
    people [4422] = Person("Fred", "[email protected]", 100); 
    people [88] = Person("John", "[email protected]", 35); 
    // insert a different way ... 
    people.insert(make_pair(42, Person("Abigail", "[email protected]", 22))); 

    // best to declare this on its own line :) 
    fPrint fo(cout); // instance of function output object 

    // print everyone (calls a function object to print) 
    cout << "Printing all using fPrint ..." << endl; 
    for_each(people.begin(), people.end(), fo); 

    // find someone by key 
    cout << "Finding person 4422 ..." << endl; 

    people_map::const_iterator i = people.find(4422); 

    if (i == people.end()) 
     cout << "Not found." << endl; 
    else { 
     fo(*i); // dereference and print 

     // another way of printing - 

     // key itself is the "first" part of the map pair ... 
     cout << "Found key = " << i->first << endl; 

     // person object is the "second" part of the map pair... 

     cout << "Found name = " << i->second.GetName() << endl; 
    } 

    // Note, this will not work: 
    // fPrint (cout) (*i); 

    // However this will: 
    // 0, fPrint (cout) (*i); 

    // However I think the extra zero is a bit obscure. :) 

    // An alternative way of finding someone. 
    // Note - this will add them if they are not there. 
    // Since this is a reference changing it will change the person in the 
    // map. Leave off the & to get a copy of the person. 

    Person & p = people [1234]; 

    cout << "Person 1234 has name " << p.GetName() << endl; 

    // Example of erasing an element correctly ... 
    // If we did the j++ as part of the for loop we would end up 
    // adding 1 to an iterator that pointed to an element that was 
    // removed which would lead to a crash. See Josuttis p 205. 

    cout << "Erasing people of age 100" << endl; 

    for (people_map::iterator j = people.begin(); j != people.end();) { 
     if (j->second.GetAge() == 100) 
     { 

      people.erase(j++); // iterator is advanced before the erase occurs 

     } 
     else 
      ++j; // advance the iterator 
    } // end of erase loop 


    // now display who is left 
    cout << "Printing people left after erase ..." << endl; 
    for_each(people.begin(), people.end(), fo); 

    return 0; 
} // end of main 
+0

我认为这是一个重复http://stackoverflow.com/questions/596162/can-you-remove-elements-from-a-stdlist-while-iterating-through-it – Anna

回答

1

擦除使迭代器无效到擦除元素。

if (j->second.GetAge() == 100) 
     { 
      temp = j++; 
      j--; 
      people.erase(j); // iterator is advanced before the erase occurs 
      j=temp; 

     } 

这不起作用,因为您将temp设置为等于j的旧值,因此您将继续使用失效的迭代器。后增量的结果是操作数的原始值。

我想你也可以像这样做,这在功能上相同的工作代码,但它不使用后增量的临时结果:

if (j->second.GetAge() == 100) { 
    temp = j; 
    ++j; 
    people.erase(temp); 
} 
+0

我也试过用temp = ++ j;并按预期工作! – sachin

1

删除无效的迭代器,这就是为什么使用后缀增量。它不会通过高级迭代器进行擦除,它会通过当前的迭代器,但会以副作用的方式前进。

people_map::iterator erase_it = j++; 
people.erase(erase_it); 

你有一个postfix增量的问题,这就是你的尝试失败的原因。

int i = 1; 
int j = i++; // j == 1, i == 2