为啥我指向向量第一个元素的指针会丢失?

Posted

技术标签:

【中文标题】为啥我指向向量第一个元素的指针会丢失?【英文标题】:Why does my pointer to the first element of a vector get lost?为什么我指向向量第一个元素的指针会丢失? 【发布时间】:2019-10-17 08:13:05 【问题描述】:
#include <iostream>
#include <vector>
//#include <string>

struct Point 
    Point(int _x, int _y) 
        x = _x;
        y = _y;
    

    int x;
    int y;
    Point *parent;
;

int main() 

    Point start(3, 4);
    std::vector<Point> points; 
    points.push_back(start);
    std::cout << points.back().x << "," << points.back().y << "\n";

    Point one(4, 5);
    one.parent = &points.at(0);
    //std::cout <<  "testing: " << one.parent->x << "," << one.parent->y << "\n";
    points.push_back(one);
    std::cout << "One: " << points[1].x << "," << points[1].y << "\n";
    std::cout << "One's parents: " << points[1].parent->x << "," << points[1].parent->y << "\n";

    Point two(10, 3);
    two.parent = &points.back();
    points.push_back(two);
    std::cout << "Two: " << points[2].x << "," << points[2].y << "\n";
    std::cout << "Two's parent: " << points[2].parent->x << "," << points[2].parent->y << "\n";

    Point three(12, 7);
    three.parent = &points[1];
    points.push_back(three);
    std::cout << "Three: " << points[3].x << "," << points[3].y << "\n";
    std::cout << "Three's parents: " << points[3].parent->x << "," << points[3].parent->y << "\n";


    return 1;

我得到以下结果: 3,4 一:4,5 父母:0,0 二:10,3 两人的父母:4,5 三:12,7 三人的父母:4,5

即使我将一个父点指向向量的第一个元素,该值最终还是 0,0。但是,其他指针指向我想要的元素。

【问题讨论】:

一旦你这样做了points.push_back(one);std::vector被允许重新分配保存所有数据的整个数组,所以这意味着如果你之前存储了指针,例如:&amp;points.back(),它可能不再是有效。 注意:如果您需要扩展代码,可以扩展此问题的更安全的解决方案是将vector 设为std::vector&lt;std::shared_ptr&lt;Point&gt;&gt;,并让parent 成为Point 的成员也是std::shared_ptr&lt;Point&gt;(假设它是DAG;如果它可以有循环,则需要std::weak_ptr&lt;Point&gt;,并且管理变得更加困难)。这避免了Points 和存储它们的vector 之间的紧密耦合(其中parent 仅在有效的全局vector 的上下文中才有意义)。 明智地使用std::make_shared(这既更安全,又允许直接std::shared_ptr无法做到的性能优化),它不会增加太多开销,而且可以让你解放相当多。 【参考方案1】:

std::vector 有一个capacity。如果添加超出向量当前容量的元素,vector 可能会决定分配一个更大的块,移动现有元素,然后添加新元素。你的情况一定也发生过这种情况。

您可以使用reserve 提升现有向量的容量。这还不会添加额外的元素;它只是准备向量。

【讨论】:

要明确:它可能会为额外元素的原始实例结构消耗内存(肯定是虚拟地址空间,实际 RAM 占用的数量非常依赖于实现),因此对于std::vector&lt;Point&gt; 在 OP 的情况下,调用 reserve 通常会保留 12-16 字节的内存(取决于指针宽度)乘以保留大小。它不必初始化内存,对于实例间接在堆上存储昂贵属性的类,该成本不会立即支付,但它必须保留原始内部数组。【参考方案2】:

虽然 MSalters 正确解释了问题的原因,以及针对这种特殊情况的可能解决方案,但一般做法有点不同。因为vector 可能随时重新分配,所以存储指向其元素的指针通常是个坏主意。 您可以改用索引,无论重新分配如何,它都是有效的;或者你可以考虑使用不同的数据结构,比如std::liststd::list 的元素在其整个生命周期中都保持在同一位置。

【讨论】:

注意:如果您知道 vector 的最终或最大大小,只要有问题的元素,适当的reserve 调用可以确保有效指针保持有效没有移动/更换。我不会说存储指向 vector 元素的指针总是是一个坏主意,只是如果你不能将 reserve/resize vector 一次存储到它永远不会超过的大小。 @ShadowRanger 你是对的。但是这样做你必须 100% 确定将来没有人愿意在 vector 中添加更多元素,比如在初始开发后的 5 年或 10 年。当产品需求可能演变成完全不同的东西时。 @Jurlie:好的设计是将该向量封装在一个类中,并具有明确定义的外部接口。如果产品需求发生变化,则可以考虑该类是否可以按原样使用、需要更新还是需要重写。

以上是关于为啥我指向向量第一个元素的指针会丢失?的主要内容,如果未能解决你的问题,请参考以下文章

向量的变化第一指针

取消引用指向访问元素的向量指针

获取指向 LLVM-IR 中数组第一个元素的指针

访问指向多维向量的指针向量中的元素

指向结构的指针向量

指向较大 unsigned char 数组的每个第 n 个元素的指针数组