c – 使用std容器的稳定内存地址(如vector,list,queue,…)

前端之家收集整理的这篇文章主要介绍了c – 使用std容器的稳定内存地址(如vector,list,queue,…)前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
参见英文答案 > Iterator invalidation rules4个
注意:我没有意识到指针被认为是迭代器,因此有人可能会认为我所谓的缺乏内存地址稳定性应该称为迭代器失效.请阅读副本,以获得更抽象和更健全的问题.

我的问题与这个问题有关:C++ reference changes when push_back new element to std::vector.

我想使用一组对象,为简单起见,这些对象在内存中只存在一次.因此,我想使用一个容器,如std :: vector,来存储所有对象一次.然后我将使用指向其他结构中的对象的指针.不幸的是,std :: vector可能会改变它元素的内存地址,因此使用指向这些元素的指针是不明确的.我需要指针,因为我想使用其他结构来引用这些对象,比如std :: priority_queue或其他std数据结构.

在特定情况下,对象是图算法中连接的标签,这意味着它们是在整个算法中创建的,因此不能预先分配.这意味着std :: vector是不够的,因为它可能会重定位其内容,使指向std :: priority_queues或其他数据结构中可能存在的这些标签的指针无效.

但是,我需要标签的唯一时刻是创建它们的时候或者我可以从包含数据结构以外的数据结构访问它们.因此,我永远不需要从容器中获取第n个元素,我只需要能够将对象保留在堆栈或堆上,并在创建它们时获取指针以在其他结构中使用它.最后,当容器从堆栈中弹出时,其中的元素需要很好地清理.我认为std :: list可能是合适的,因为我对抽象链表的知识永远不需要重新分配;允许稳定的指针.

但是,我无法找到std :: lists的指针稳定性为何.也许有一些优越的东西,一些容器类完全符合我的要求.当然,我总是可以使用new,将所有指针追加到std :: list并迭代在最后执行删除.但这不是我喜欢的方式,因为它需要更多的内存管理,因为我认为应该只需要获得稳定的指针.

问题:std :: list指针是否稳定?有比std :: list更好的解决方案吗?

为了说明这个问题,我也做了这个例子:http://ideone.com/OZYeuw.用std :: vector替换std :: list,行为变得不确定.

#include <iostream>
#include <list>
#include <queue>
#include <vector>

struct Foo {
    Foo(int _a) : a(_a) {}
    int a;
};

struct FooComparator {
    bool operator()(Foo *a,Foo *b) { return a->a < b->a; }
};

int main() {
    std::list<Foo> foos;
    //std::vector<Foo> foos; // when used instead,the behavIoUr will become undefined
    std::priority_queue<Foo *,std::vector<Foo *>,FooComparator> pq;

    // Simulate creation and 'containment' of objects,while they are being processed by other structures.
    for(int i=0; i<100; ++i) {
        foos.push_back(Foo((100-i) % 10));
        pq.emplace(&foos.back());
    }

    while(not pq.empty()) {
        std::cout << pq.top()->a << " "; // the dereference -> may segfault if foos is not *pointer stable*
        pq.pop();
    }

    std::cout << std::endl;
    return 0;
}

解决方法

对于所有标准容器,指针/引用和迭代器失效都有特定的规则.甚至std :: vector< T>如果您可以预测最大容量,可能是一个选项:

>在std :: vector< T>的末尾添加/删除对象保持指针和迭代器稳定,除非std :: vector< T>需要重新分配其内部结构.也就是说,指针和迭代器只有在添加对象和v.size()== v.capacity()时才会失效.您可以使用v.reserve(n)来保留空间.
>在std :: deque< T>的任一端添加/删除对象保持指针稳定,但确实使迭代器无效.
>在std :: list中添加/删除对象< T>保持指针和迭代器稳定.

显然,删除对象的指针和迭代器在所有情况下都是无效的.但是,指向其他对象的指针和迭代器遵循上述规则.

操作和内存的开销按容器显示的顺序增加.也就是说,理想情况下你使用的是std :: vector< T>并提前分配足够的内存以保持对象稳定.如果无法预测所需的最大大小,则std :: deque< T>是下一个最佳选择:std :: deque< T>是一个固定大小的数组的数组,即每个对象开销相对较小并且内存分配相对较少.只有当你需要保留指针和迭代器表时,才能预测容器的大小,std :: list< T>是一个合理的选择.要降低每个对象的开销,可以考虑使用std :: forward_list< T>它与std :: list< T>具有相同的失效约束但只能在一个方向上移动,并且使用起来有点不方便.

我将使用std :: vector< T>有足够的保留记忆.如果我不能,我会这样做,我可以使用std:vector< T>.只有在真的不可能的情况下,我才考虑使用std :: deque< T>用于存储对象. ……我很少使用std :: list< T>因为几乎没有理由使用它.

猜你在找的C&C++相关文章