如何保持引用/指针/链接到vector元素后更改为vector

How to maintain reference/pointer/link to vector element after change to vector?

本文关键字:vector 元素 何保持 引用 指针 链接      更新时间:2023-10-16

我有一个自定义类的std::vector(为了简单起见,在示例中使用int)。我想保留一个指向向量成员的引用/指针/链接/其他。然而,vector经常删除和添加元素。

为了说明我的观点,在下面的示例中,我接受指向vector的第二个元素的引用或指针。我使用引用/指针来增加所选元素的值。然后擦除第一个元素,并使用ref/指针再次自增。

参考例子:

std::vector<int> intVect = {1,1,1};
int& refI = intVect.at(1);
refI++;
intVect.erase(intVect.begin());
refI++;

智能指针示例:

std::vector<int> intVect2 = {1,1,1};
std::shared_ptr<int> ptrI = std::make_shared<int>(intVect2.at(1)) ;
*ptrI = *ptrI +1;
intVect2.erase(intVect2.begin());
*ptrI = *ptrI +1;

我想要发生的是最终引用元素的值为3,最终向量由{3,1}组成。然而,在参考示例中,最终向量是{2,2},在指针示例中,最终向量是{1,1}

理解指针本质上是一个内存地址,我可以理解为什么这个方法可能不可能,但如果它不知何故是,让我知道。

更重要的问题是,可以使用什么替代方法或结构来允许某种形式的ref/指针/链接/其他指向包含它的向量(或其他结构)的元素(无论是值还是对象),在向其添加成员或从其删除成员后是可行的?

额外学分:

我实际工作的对象有一个position属性。我有第二个结构,它需要跟踪对象,以便快速查找哪个对象在哪个位置。我目前使用网格(向量的向量)来表示可能的位置,每个位置都包含对象向量的索引,用于当前位置的对象。但是,当从向量中删除对象时(这种情况非常频繁,每次迭代多达数百次),我目前的方法是遍历每个网格位置并递减大于已删除索引的索引,这种方法既慢又笨拙。在上下文中对这个问题的其他想法非常感谢,但我的关键问题与上述示例有关。

一个可能的选择是让vector存储std::shared_ptr对象,并发出std::weak_ptrstd::shared_ptr对象来引用所讨论的对象。

std::vector<std::shared_ptr<int>> ints;
for(size_t i = 0; i < 10000; i++) {
    ints.emplace_back(std::make_shared<int>(int(i)));
}
std::weak_ptr<int> my_important_int = ints[6000];
{
    auto lock = my_important_int.lock();
    if(lock) std::cout << *lock << std::endl;
    else std::cout << "index 6000 expired." << std::endl;
}
auto erase_it = std:remove_if(ints.begin(), ints.end(), [](auto & i) {return (*i) > 5000 && ((*i) % 4) != 0;});
ints.erase(erase_it, ints.end());
{
    auto lock = my_important_int.lock();
    if(lock) std::cout << *lock << std::endl;
    else std::cout << "index 6000 expired." << std::endl;
}
ints.erase(ints.begin(), ints.end());
{
    auto lock = my_important_int.lock();
    if(lock) std::cout << *lock << std::endl;
    else std::cout << "index 6000 expired." << std::endl;
}

应该打印出:

6000
6000
index 6000 expired.

存储键/值对的容器可能适合您。例如:std::mapstd::unordered_map

当使用这些容器时,您将通过存储键来保持对所需对象的引用。如果要修改该对象,只需使用该键在容器中查找即可。现在,您可以随心所欲地添加/删除其他对象,而不会影响正在讨论的对象(假设添加/删除的对象具有唯一键)。

如果有一种方法可以让您继续使用矢量并改变管理对象的方式,那么您将不会获得比现在更好的性能。

否则,您可以使用稳定向量(这里是boost版本)。它本质上是一个指针向量,这赋予了它迭代器和引用的稳定性。这意味着迭代器(指针)和对元素的引用不会因除删除元素本身以外的任何操作而失效。

当然,这有一些很大的缺点,主要是在性能上。两个主要的性能问题是每次想要访问元素时都要遍历一个指针,以及元素不是连续存储的(这当然会影响迭代的速度)。

然而,与其他指针较多的数据类型(列表、集合、映射)相比,它也有优势。主要是,它在常量时间内执行查找和回推,尽管它比普通向量慢。

然后,如果你真的需要性能,你可能想要保留你的矢量,并重新考虑你的设计。