我有以下代码试图转储存储在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 ++队列做了同样的事情并没有观察到这个问题。
答案 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
vector
与erase(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)。由于纯粹的机会,你现在正在获得正确的价值!这就是为什么你总是需要在流行音乐之前复制或移动。