【问题标题】:Problems with C++ containersC++ 容器的问题
【发布时间】:2011-04-08 09:41:48
【问题描述】:

我在 C++ 程序中有一个 std::list,其中包含 A 类的对象。 假设我有 10 个对象。我有对存储在另一个数据结构中的第 6 个对象的引用,例如 ref_6。假设我需要从列表中删除第 8 个元素。为此,我将使用 pop_front 8 次并将 8 个对象存储在一个向量中,并使用 push_front 7 次将前 7 个元素插入到列表中,所以现在我的结果列表将有 9 个元素。现在,当我尝试访问存储在 ref_6 中的对象时,这是第 6 个元素,我做不到。此参考中有一些垃圾值。 我假设当我执行 pop 和 push 时,同一对象的内存位置会发生变化。我该如何处理?

【问题讨论】:

    标签: c++ list memory-management stl containers


    【解决方案1】:

    你为什么要以这种方式擦除东西? D:这不是堆栈。列表的全部要点(以及唯一点*)是您可以在恒定时间内删除任何元素。 (虽然发现它是线性的。)

    这样做:

    typedef std::list<T> list_type;
    
    list_type mylist; // populate it
    
    list_type::iterator iter =  mylist.begin();
    std::advance(iter, 8); // move to 8th item
    
    mylist.erase(iter); // erase it
    

    并且没有其他迭代器失效。 (事实上​​,删除一个元素会使对它的任何引用无效。)


    *您可能甚至不应该使用列表。列表在学习数据结构方面很好,但它们非常糟糕。

    【讨论】:

    • 这样,列表大小(内存开始和内存结束位置)保持不变,只是一个元素被删除/无效。不是吗?
    • 我很好奇您的 cmets 在现实生活中的列表使用情况----正如您所说,它在 STL 中的原因是:在恒定时间内插入或删除任何元素
    • @cyrux:是的,链表只会删除那个元素,其他的都是有效的。 @Dbger:标准库包含它是因为它是一种常见的数据结构,并且确实有它的用途。但这些用途很少;您的主要操作基本上是迭代容器并经常删除元素,并且经常执行那个。它具有糟糕的缓存一致性以及线性访问时间,使其在实践中非常缓慢;即使你半经常地从中间删除一些东西,vector/deque 也会因为局部性而表现得更好。
    • “列表...它们非常糟糕”。我不知道。我发现保证 std::list 使 w.r.t.迭代器有效性、O(1) 复杂度等非常好。
    【解决方案2】:

    列表将其元素存储在不连续的内存块中,当元素从列表中删除时,这些内存块将被释放。所以引用(它被简单地实现为一个指针)指向一个内存已经被释放的元素。

    从列表中删除给定元素的更简单方法是让迭代器指向它并使用方法

    std::list::iterator = /*somehow get the iterator to the 8th element*/
    yourList.erase(8th_element_iterator);

    第一步(获取第 8 个元素的迭代器)可以通过获取列表开头的迭代器并将其向前推进 7 个位置来完成:

    std::list::iterator first_iter = yourList.begin();
    std::list::iterator 8th_iter = std::advance(first_iter, 7);

    【讨论】:

      【解决方案3】:

      这里有些腥味...您将T 类型的对象按值存储在std::list&lt;T&gt; 中。您在其他地方保留对这些对象的引用。那是对的吗?如果是,我看到了几个问题... 对列表的许多操作可能会使存储的引用无效,因为std::list&lt;T&gt; 仅保证T 类型元素的 的顺序。如果您想在多个位置存储对这些元素的引用,请使用std::tr1::shared_ptr&lt;T&gt;std::list&lt;std::shared_ptr&lt;T&gt; &gt;。然后,您可以安全地删除或添加(甚至重新定位)列表中的元素,并且保留在其他位置的引用仍然有效。注意存储std::list&lt;T&gt;iterators,问题是一样的。

      【讨论】:

      • 你说我按值持有类型 T 的对象,但在 stl_list 中 push_front 有这个原型 void push_front(const value_type& __x)。所以这不是持有参考,与价值相同。另外,在另一个地方,当我说我持有参考时,我实际上是在使用指针,我认为这是导致问题的原因。所以你说如果我使用了 shared_ptr 列表,那么我可以删除和添加元素,并且我对这些元素的引用可以正常工作。如果我更改列表中元素的顺序,例如使用 pop_front 获取元素然后 push_back ,这是否成立?
      【解决方案4】:

      我指的是您的回复。对不起,我没有弄好帐户的事情... 请考虑以下几点:

      std::list<A> tList;
      A tA;
      
      tList.push_back(tA);
      assert(&tA == &tList.back()); // boom!
      
      A *tAPtr = &tList.front();
      
      tList.erase(tList.front());
      // try to access tAPtr:
      tAPtr->Foo(); // boom! (probably)
      

      关键是A 的实例是按值存储的(= 复制),因此您所做的事情本质上是不安全的。请改用std::list&lt;std::tr1::shared_ptr&lt;A&gt; &gt;

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-08-19
        • 2019-09-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多