当使用head元素的引用时,C ++优先级队列表现得很糟糕

时间:2013-07-26 20:12:25

标签: c++ stl priority-queue

我有以下代码试图转储存储在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()

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

P.S。我为C ++队列做了同样的事情并没有观察到这个问题。

2 个答案:

答案 0 :(得分:1)

更改priority_queue后(例如弹出),所有引用都会被激活。所以它是未定义(?)行为

会发生什么:

您可以参考top元素。它存储为基础std::vector的第一个元素,因此您只需链接到vector中的第一个元素。当popping您将vector重新排列为新状态并删除了之前的top元素时。新的top元素应该是vector中的第一个元素。因此,如果vector位于同一位置,则引用指向新的顶部

<强>加了:

一些参考文献:

make_pop: §23.6.4.3/4

  

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

§25.4.6.2/2

  

E ff ects:首先将位置中的值与最后位置的值进行交换 - 1并使其成为   [first,last - 1]成堆。

我没有找到pop_back vectorerase(end() - 1)完全相同的信息(仅限字符串),但似乎是真的。

§23.3.6.5/3

  

迭代器擦除(const_iterator位置);
  迭代器擦除(const_iterator优先,const_iterator最后);
  E ff ects:在擦除点或之后使迭代器和引用无效

所以似乎依赖于引用将指向pop之后的新顶部是有效的,因为即使重新分配似乎也是不可能的。但无论如何使用它并不是一个好主意。


*所有对N3242的引用

答案 1 :(得分: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)。由于纯粹的机会,你现在正在获得正确的价值!这就是为什么你总是需要在流行音乐之前复制或移动。