是什么原因导致存储的std :: list :: iterator无效?

问题描述

编辑:以下代码可以在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无效?

删除迭代器指向的元素,无论是通过clearpop_Xerase还是破坏列表。 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

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...