当对头元素使用引用时,C++ 优先级队列的行为很奇怪

Posted

技术标签:

【中文标题】当对头元素使用引用时,C++ 优先级队列的行为很奇怪【英文标题】:C++ priority queue behaves wierdly when using reference for the head element 【发布时间】:2013-07-26 20:12:24 【问题描述】:

我有以下代码试图转储存储在 C++ 中的 priority_queue 中的值:

priority_queue<Point, vector<Point>, myCmp> pq;
int i;
for (i = 0; i < 10; i++) 
    Point p(i,i);
    pq.push(p);

const Point& p0 = pq.top();
while(!pq.empty()) 
    cout<<p0.x<<p0.y<<endl;
    pq.pop();
//I'm getting output like "00 11 22 33 ... 99"

正如代码中的注释所说,每次队列弹出一个值时,我的引用变量 p0 都会不断变化。这对我来说没有意义,因为我认为 p0 应该始终是对 (0,0) 对象的引用,即开始时队列的前面。我知道我可以使用

Point p0 = pq.top()

获取前端元素的副本并避免该问题。但是,有人可以解释使用引用的问题吗?

附:我对 C++ 队列做了同样的事情,但没有发现这个问题。

【问题讨论】:

这是一种可枚举的标准做法;在迭代它们时,修改它们并不能保证引用将继续有效。 p0 指向存储在队列前面的对象。没有保证在弹出之后保持相同的位置(实际上,如果您使用 list 作为底层容器,或者它决定重新分配 vector,那么在弹出之后您将不会指向任何内容! )。这对您“有效”只是因为矢量没有调整大小。 【参考方案1】:

实际上,我将在评论中给出更深入的答案。

所以当你得到一个引用时,你实际上只是得到了一个指向前面元素的指针。 std::vector 是底层容器,因此您拥有的代码(在功能上)与您拥有的代码更相似:

vector<Point> pq;
int i;
for (i = 0; i < 10; i++) 
    Point p(i,i);
    pq.push_back(p);

Point* p = &pq[0];

while(!pq.empty()) 

    cout<<p->x<<p->y<<endl;
    pq.erase(pq.begin(),pq.begin()+1);
//I'm getting output like "00 11 22 33 ... 99"

现在没有保证 p* 在您开始弄乱值后指向向量的第一个成员。确实,如果我添加一个 pq.resize(100);在 p 分配之后,虽然 pq[0] 仍然等于 (0,0),但 p 指向外部随机未保留内存(读取输出垃圾或出现 seg-fault)。由于纯粹的机会,您现在正在获得正确的价值!这就是为什么你总是需要在弹出之前复制或移动。

【讨论】:

【参考方案2】:

在您更改 priority_queue(例如通过弹出)后,所有引用都将失效。所以这是未定义(?)行为

会发生什么:

您获得对顶部元素的引用。它存储为底层std::vector 的第一个元素,因此您只需链接到vector 中的第一个元素。当popping 重新排列vector 到新状态时,删除了前一个顶部元素。新的顶部元素应位于 vector 的首位。所以,如果vector 在同一个地方引用指向新的顶部

添加:

一些参考资料:

制作流行音乐: § 23.6.4.3/4

void pop(); 4 效果: pop_heap(c.begin(), c.end(), comp); c.pop_back();

§ 25.4.6.2/2

效果:将位置 first 中的值与位置 last 中的值交换 - 1 并使得 [first,last - 1) 放入一个堆中。

我没有发现 pop_back for vectorerase(end() - 1) 完全相同(仅适用于字符串)的信息,但它似乎是真的。

§ 23.3.6.5/3

迭代器擦除(const_iterator position); 迭代器擦除(首先是 const_iterator,最后是 const_iterator); 效果:使迭代器和引用失效在擦除点或之后

因此,在pop 之后依赖引用将指向新的顶部似乎是有效的,因为即使重新分配似乎也是不可能的。但无论如何都不要使用它。


*所有参考 N3242

【讨论】:

以上是关于当对头元素使用引用时,C++ 优先级队列的行为很奇怪的主要内容,如果未能解决你的问题,请参考以下文章

优先队列

POJ2431(优先队列)

c++ STL queue:deque+优先队列

C/C++优先级队列

具有自定义比较功能的 C++ 优先级队列在 Push() 上行为不正确

队列和优先队列