C++:指针向量丢失了push_back()

问题描述 投票:0回答:3

在我的代码a中,有节点对象的全局向量和节点指针的局部向量:

#include<cstdio>
#include<cstdlib>
#include<vector>

using namespace std;

class Node {
    int n;

public:
    Node(int i) : n(i);
    int getN() { return n; }
};

vector<Node> v;

int main() {
    vector<Node*> p;
    v.push_back(Node(1));
    p.push_back(&v[0]);
    printf("first node id : %d\n", (*p[0]).getN());

    return 0;
}

I将节点对象插入全局向量,并将该对象的指针插入局部向量。我上述代码的输出是:

first node id : 1

,但是,如果我将主要功能更改为:

int main()
{
    vector<Node*> p;
    v.push_back(Node(1));
    p.push_back(&v[0]);
    v.push_back(Node(2));
    p.push_back(&v[1]);
    printf("first node id : %d\n", (*p[0]).getN());

    return 0;
}

代码打印一个垃圾值:

first node id : 32390176

我无法弄清楚这个问题。

vector
数据结构是否会在插入后会更改每个对象的引用? 我该如何解决?

c++ pointers vector
3个回答
20
投票

“向量会在插入后改变参考吗?”

是的,是的。当您向其添加其他元素时,可能会重新分配其(堆)存储 - 从而使所有指针无效:

术语[读:指针]无效

(用于操作)std::vector

push_back

...如果向量更改了容量,则所有这些都[即所有迭代器都是无效的]。如果没有,只有

emplace_back
“我该如何修复?”

如果向量的容量由于插入而不会变化,则上述无效规则不会适用 - 因为向量不会不必要地重新分配存储。因此,如果您将矢量的能力预先设置为示例2(例如用
end()

),则指针将保持有效。如果您不提前知道尺寸,但是可以延迟第二个矢量的构建(带有指针),则不必保留,插入最后一个元素后,您只会拥有大小。

然而,上面的方法是不受欢迎的。如果您要使矢量

constant

-至少在构造和使用第二个向量的函数范围内 - 您将有很大的保证非算法。另外,如果您可以提前确定尺寸,则可以使用

v.reserve(2)

,并且将指针在该容器的存储中使用更合适: siterator无效

作为规则,在整个数组的整个生命周期中,迭代器的迭代器永远不会无效。
您还可能考虑将

Indices
存储到您的向量中(尽管也可能在其中缩小,使索引无效,或者您可能会在中间插入元素等)。

,我怀疑您实际上可能不想做任何事情,即,这似乎是一个不太好的解决方案,可以完全以不同的方法处理。 PS-如果向量具有

custom分配器

,那么我写的所有内容可能都是无关紧要的。

yyes,在向量上的a

std::array将所有参考文献(和指针)无效,如果必须重新分配该矢量中的元素。 有多种方法可以解决这个问题。 如果您知道您的向量将具有特定数量的节点,则可以使用push_back()

。 在您的示例中,您可以保留两个要素:

reserve()

这将确保向量已预先列出足够的存储,因此它不需要重新分配。 如果您不提前知道尺寸,那么您必须更改有关方法的内容。 您可能会使用int main() { v.reserve(2); . . . } 而不是

std::deque

6
投票
std::vector

std::deque
没有无效的引用。 您可以存储索引而不是指针。 或者,您可能需要在制作指针之前将所有节点推入矢量。
push_back()

您正在做的是向量的未定义行为

int main() { v.push_back(Node(1)); v.push_back(Node(2)); vector<Node*> p; p.push_back(&v[0]); p.push_back(&v[1]); printf("first node id : %d\n", (*p[0]).getN()); return 0; }

p

,因为向量可以在存储其对象的位置更改。

A
v
的内存是连续的,因此,在许多
std::vector
之后,它可能必须分配新的块内存并将其内容复制到新块中。 这将使所有指向旧内存位置的指针无效。

您偶然发现了C ++的伟大“黑暗角落”之一:伟大的“迭代无效”。绝对熟悉这一点:

术语无效规则

5
投票
特别是,您正在打击这一现实:

  
矢:插入点之前的所有迭代器和参考
  不受影响,没有新的容器尺寸大于以前
  容量(在这种情况下,所有迭代器和参考都无效)

[23.2.4.3/1]

(重点是我的)
现在,关于您的问题。您可以确保向量永远不会重新分配。或者,您可以使用没有此问题的其他容器。所有容器类型之间都有妥协,具体取决于您的需求。彻底查看另一个问题并做出明智的决定。
    

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.