问题描述
编辑:以下代码可以在https://wandbox.org/permlink/1Qry83quzoDveYDi上运行
我根据评论实施了各种建议,但不幸的是,我仍然不清楚为什么删除 此特定 std::list::iterator
(第86行)中的项目会在运行。下面给出的所有解释似乎都肯定了迭代器在这一点上仍然应该有效。
给我的印象是,将项目插入列表中时,std :: list中的项目迭代器不会失效(请参阅this excellent post)。
但是,在下面的代码中,该行items.at(noOfitems-2)->erase(iter++);
(第86行)
使用
使程序崩溃
malloc: *** error for object 0x100778b28: pointer being freed was not allocated
。
能否请您帮助我理解为何该迭代器std::list<std::string>::iterator
无效(为何),以及如何使它工作而又无需反复找到它?
我可能会误解错误了吗?
#include <iostream>
#include <iostream>
#include <vector>
#include <random>
#include <unordered_set>
#include <set>
#include <unordered_map>
#include <map>
#include <list>
#include <utility>
#include <chrono>
#include <sstream>
#include <tr1/memory>
class Increment;
struct Item;
struct Pairhash {
public:
template <typename T>
std::size_t operator()(const T &x) const
{
return std::hash<T>()(x) ^ std::hash<T>()(x);
}
};
struct Item {
Item() = default;
std::string name;
int counter;
Item(std::string name) : counter(0)
{
this->name = name;
}
bool operator==(const Item& p) const
{
return (this->name == p.name);
}
bool operator<(const Item& p) const
{
return (this->counter < p.counter);
}
};
class Increment {
private:
std::unordered_map<std::string,std::pair<std::list<std::string>::iterator,std::shared_ptr<Item> >,Pairhash > itemMap;
std::vector<std::shared_ptr<std::list<std::string>>> items;
public:
Increment() = default;
std::list<std::string>::iterator insertItem(std::string & name,int noOfitems)
{
if (noOfitems > items.size())
{
items.emplace_back(std::make_shared<std::list<std::string>>(std::initializer_list<std::string>{ name }));
return items.back()->begin();
}
else
{
items.at(noOfitems-1)->emplace_back(name);
return items.at(noOfitems-1)->rbegin().base(); //Last position in list
}
}
std::list<std::string>::iterator adjustItemPosition(std::string & name,int noOfitems,std::list<std::string>::iterator & iter)
{
if (noOfitems > items.size())
{
std::list<std::string> temp{name};
items.push_back(std::make_shared<std::list<std::string>>(temp));
}
else
{
items.at(noOfitems-1)->emplace_back(name);
}
/* // Works as expected
auto itr = std::find(items.at(noOfitems-2)->begin(),items.at(noOfitems-2)->end(),name);
if (itr != items.at(noOfitems-2)->end())
{
items.at(noOfitems-2)->erase(itr);
}
*/
items.at(noOfitems-2)->erase(iter++); //TODO Crashes
return items.at(noOfitems-1)->rbegin().base(); //Last position in list
}
void incrementByOne(std::string name)
{
auto it = itemMap.find(name);
if (it != itemMap.end()) //Item already in map
{
it->second.second->counter++;
it->second.first = adjustItemPosition(name,it->second.second->counter,it->second.first);
}
else //New item to be inserted
{
std::shared_ptr<Item> temp = std::make_shared<Item>(Item(name));
temp->counter = 1;
std::list<std::string>::iterator listIter = insertItem(name,1);
itemMap.emplace(name,std::make_pair( listIter,temp));
}
}
std::string printTop10() const
{
std::stringstream ss;
auto count(0);
for (auto it = items.rbegin(); it != items.rend(); ++it)
{
for (auto item : **it)
{
if (count == 10)
{
break;
}
ss << "We have " << itemMap.at(item).second->counter << " " << item << std::endl;
++count;
}
}
return ss.str();
}
};
int main(int argc,const char * argv[]) {
Increment incrementer;
std::vector<std::string> names{ "Bananas","Apples","Peaches","Durians","Hazelnuts","Avocados","Pineapples","Cherries","Almonds","Olives","Eggs","Yoghurts","Peas","Blueberries" };
for (int i = 0; i < 100; ++i) {
incrementer.incrementByOne(names.at(i%10));
}
std::cout << incrementer.printTop10() << std::endl;
return 0;
}
解决方法
是什么原因导致存储的std :: list :: iterator无效?
删除迭代器指向的元素,无论是通过clear
,pop_X
,erase
还是破坏列表。 assign
使对该列表中所有元素的迭代器无效。
您的代码中至少有一个问题:
std::list<std::string> temp;
temp.push_back(name);
items.emplace_back(std::make_shared<std::list<std::string>>(temp));
return temp.begin();
在这里,您正在将迭代器返回给淘汰列表中的element。您可能还会遇到其他问题。
,更换return items.at(noOfitems-1)->rbegin().base();
与return std::next(items.at(noOfitems-1)->end(),-1);
第63和86行修复了崩溃问题。
工作解决方案:https://wandbox.org/permlink/I68Szb0XMRKsPZqp
#include <iostream>
#include <iostream>
#include <vector>
#include <random>
#include <unordered_set>
#include <set>
#include <unordered_map>
#include <map>
#include <list>
#include <utility>
#include <chrono>
#include <sstream>
#include <memory>
class Increment;
struct Item;
struct Pairhash {
public:
template <typename T>
std::size_t operator()(const T &x) const
{
return std::hash<T>()(x) ^ std::hash<T>()(x);
}
};
struct Item {
Item() = default;
std::string name;
int counter;
Item(std::string name) : counter(0)
{
this->name = name;
}
bool operator==(const Item& p) const
{
return (this->name == p.name);
}
bool operator<(const Item& p) const
{
return (this->counter < p.counter);
}
};
class Increment {
private:
std::unordered_map<std::string,std::pair<std::list<std::string>::iterator,std::shared_ptr<Item> >,Pairhash > itemMap;
std::vector<std::shared_ptr<std::list<std::string>>> items;
public:
Increment() = default;
std::list<std::string>::iterator insertItem(std::string & name,int noOfitems)
{
if (noOfitems > items.size())
{
items.emplace_back(std::make_shared<std::list<std::string>>(std::initializer_list<std::string>{ name }));
return items.back()->begin();
}
else
{
items.at(noOfitems-1)->emplace_back(name);
return std::next(items.at(noOfitems-1)->end(),-1); // versus return items.at(noOfitems-1)->rbegin().base(); //Crashes
}
}
std::list<std::string>::iterator adjustItemPosition(std::string & name,int noOfitems,std::list<std::string>::iterator & iter)
{
if (noOfitems > items.size())
{
std::list<std::string> temp{name};
items.push_back(std::make_shared<std::list<std::string>>(temp));
}
else
{
items.at(noOfitems-1)->emplace_back(name);
}
/* // Works as expected
auto itr = std::find(items.at(noOfitems-2)->begin(),items.at(noOfitems-2)->end(),name);
if (itr != items.at(noOfitems-2)->end())
{
items.at(noOfitems-2)->erase(itr);
}
*/
items.at(noOfitems-2)->erase(iter++); //TODO Crashes
return std::next(items.at(noOfitems-1)->end(),-1); //versus return items.at(noOfitems-1)->rbegin().base(); //Crashes
}
void incrementByOne(std::string name)
{
auto it = itemMap.find(name);
if (it != itemMap.end()) //Item already in map
{
it->second.second->counter++;
it->second.first = adjustItemPosition(name,it->second.second->counter,it->second.first);
}
else //New item to be inserted
{
std::shared_ptr<Item> temp = std::make_shared<Item>(Item(name));
temp->counter = 1;
std::list<std::string>::iterator listIter = insertItem(name,1);
itemMap.emplace(name,std::make_pair( listIter,temp));
}
}
std::string printTop10() const
{
std::stringstream ss;
auto count(0);
for (auto it = items.rbegin(); it != items.rend(); ++it)
{
for (auto item : **it)
{
if (count == 10)
{
break;
}
ss << "We have " << itemMap.at(item).second->counter << " " << item << std::endl;
++count;
}
}
return ss.str();
}
};
int main(int argc,const char * argv[]) {
Increment incrementer;
std::vector<std::string> names{ "Bananas","Apples","Peaches","Durians","Hazelnuts","Avocados","Pineapples","Cherries","Almonds","Olives","Eggs","Yoghurts","Peas","Blueberries" };
for (int i = 0; i < 100; ++i) {
incrementer.incrementByOne(names.at(i%10));
}
std::cout << incrementer.printTop10() << std::endl;
return 0;
}
我相信在https://stackoverflow.com/a/33851951中给出了关于为什么这样做的答案,即指向反向迭代器的指针实际上是原始引用的副本,当它退出时会被删除。范围。 http://cplusplus.github.io/LWG/lwg-defects.html#2360