丢失对容器内对象的引用

Losing references to objects inside containers

提问人:MTLaurentys 提问时间:5/28/2020 最后编辑:quamranaMTLaurentys 更新时间:5/29/2020 访问量:53

问:

我正在努力修复一段代码,即使我相信我知道原因。我确实在 Stack Overflow 上搜索了很多,很多人都有类似的问题(并且提供了很好的答案),但我仍然不确定我是否理解我的代码的问题。

我试着举一个较小的例子,但我的结构本身并不简单。我希望以下内容足够简单。

我对摘录的目标:

  • 有一个点的向量。

  • 有一个段向量。(其端点引用上述向量中的点)

  • 有一个事件向量。(保留对 exhisting 段和点的引用)

segment::segment (point &pt1, point&pt2, int i) {
   s = &pt1; e = &pt2; ind = i; // point *s, point *e, int ind
}
event::event (segment &s, point &pt, int tp) {
   seg = &s; type = tp; p = &pt; //segment *seg, int type, point *p
}
void pre_process (int size, std::vector<point>& pts, std::vector<event>& evts,
        std::vector<segment>& segs) {
    float x, y;
    for (int i = 0; i < size; ++ i) {
        std::cin >> x >> y;
        pts.push_back({x,y});
    }
    for (int i = 0; i < size; ++i) {
        int j = (i+1)%size;
        if (cmpXY(pts[i], pts[j]))
            segs.push_back({pts[i], pts[j], i});
        else
            segs.push_back({pts[j], pts[i], i});
        evts.push_back({segs[i], *segs[i].s, 0});
        evts.push_back({segs[i], *segs[i].e, 1});
        std::cout << 2*i << "\n";
        std::cout << segs[i].s << "\n";       //ALWAYS EQUAl
        std::cout << evts[2*i].seg->s << "\n";
    } // LINE X
    for (int i = 0; i < size; ++i) { //LINE Y
        std::cout << 2*i << "\n";
        std::cout << segs[i].s << "\n";        //DIFFERENT SOMETIMES
        std::cout << evts[2*i].seg->s << "\n";
    }

因此,问题在于我的对象指向的某些地址从 LINE X 更改为 LINE Y。特别是,pts 和 segs 保持不变。

根据我所知道的以及我在这里或这里所理解的,我无法获得对任何函数堆栈上对象的引用(例如,我的 for 循环)。但是,我相信容器上对象的生存期与容器本身相同。这让我相信,我应该通过函数pre_proccess和调用它的函数来保留所有对象。push_back()

C++ C++11 按引用传递

评论


答:

1赞 Jarod42 5/28/2020 #1

push_back在向量上(可能)使先前的引用/迭代器无效。

它发生在那里的循环中

if (cmpXY(pts[i], pts[j]))
    segs.push_back({pts[i], pts[j], i});
else
    segs.push_back({pts[j], pts[i], i});
evts.push_back({segs[i], *segs[i].s, 0});

你必须有足够的位置来避免重新分配(或改变逻辑)。reservesegs

评论

0赞 MTLaurentys 5/28/2020
有趣。它修复了我的测试集的问题。我此刻正试图理解为什么。如果我先构建了 segs,问题会持续存在吗?我从来没有遇到过这个问题。
2赞 Jarod42 5/28/2020
std::vector是连续的容器。一旦达到容量,它就必须重新分配,并在新缓冲区中移动以前的值,从而使对旧缓冲区的每个引用无效。一旦生成(或保留),您就不会再使引用/迭代器失效。
1赞 ichramm 5/28/2020 #2

看起来向量调整了自身大小,必须重新定位(这将使所有迭代器引用无效)。

处理前尽量使用:std::vector::reserve

void pre_process (int size, std::vector<point>& pts, std::vector<event>& evts,
        std::vector<segment>& segs) {
    pts.reserve(size);
    evts.reserve(size);
    segs.reserve(size);

    float x, y;
    for (int i = 0; i < size; ++ i) {
        std::cin >> x >> y;
        pts.push_back({x,y});
    }
    for (int i = 0; i < size; ++i) {
        int j = (i+1)%size;
        if (cmpXY(pts[i], pts[j]))
            segs.push_back({pts[i], pts[j], i});
        else
            segs.push_back({pts[j], pts[i], i});
        evts.push_back({segs[i], *segs[i].s, 0});
        evts.push_back({segs[i], *segs[i].e, 1});
        std::cout << 2*i << "\n";
        std::cout << segs[i].s << "\n";       //ALWAYS EQUAl
        std::cout << evts[2*i].seg->s << "\n";
    } // LINE X
    for (int i = 0; i < size; ++i) { //LINE Y
        std::cout << 2*i << "\n";
        std::cout << segs[i].s << "\n";        //DIFFERENT SOMETIMES
        std::cout << evts[2*i].seg->s << "\n";
}