#c #c 11 #stl
#c #c 11 #stl
Вопрос:
У меня есть код на C следующим образом :
#include <iostream>
#include <queue>
#include <list>
using namespace std;
int main(){
priority_queue<pair<int, int>> q;
q.push({8, 8});
q.push({19, 19});
q.push({23, 23});
while (q.size() > 0){
autoamp; cur = q.top();
q.pop();
cout << cur.first << " " << cur.second << " "; // returns 19 19 8 8 8 8
}
cout << endl;
list<pair<int, int>> newq;
newq.push_back({19, 19});
newq.push_back({8, 8});
newq.push_back({23, 23});
while (newq.size() > 0){
autoamp; cur = newq.front();
newq.pop_front();
cout << cur.first << " " << cur.second << " "; // returns 19 19 8 8 23 23
}
cout << endl;
return 0;
}
Повторение с list
использованием autoamp;
vs auto
приводит к тому же результату. Но я пытаюсь понять, почему, если я использую autoamp;
во время итерации по очереди приоритетов, вывод неверен. Если я изменю его на auto
правильный вывод:
priority_queue<pair<int, int>> q;
q.push({8, 8});
q.push({19, 19});
q.push({23, 23});
while (q.size() > 0){
auto cur = q.top();
q.pop();
cout << cur.first << " " << cur.second << " "; // returns 23 23 19 19 8 8
}
cout << endl;
Спасибо за любые разъяснения!
Ответ №1:
autoamp; cur = q.top();
q.pop();
Причина auto amp;
, по которой здесь не работает и auto
работает, заключается в том, что при auto amp;
этом вы получаете ссылку на объект в top()
начале очереди…
… и сразу же, в следующей строке, этот объект в верхней части очереди удаляется и уничтожается, оставляя вас с висячей ссылкой.
Использование этой ссылки с этого момента приводит к неопределенному поведению.
auto
Вместо этого используется копирование объекта, и копия продолжает существовать после уничтожения исходного объекта.
Конечно, возможно, что аналогичная ошибка при использовании с std::list
не приводит к явно неправильным результатам, но это все еще неопределенное поведение. «Неопределенное поведение» означает именно это: может случиться все, что угодно. Включая получение ожидаемых результатов, чисто случайно. Хороший инструмент статического анализа, такой как valgrind, должен быть в состоянии обнаружить эту ошибку.
Комментарии:
1. Но разве код не присваивает ссылку на
top
в каждой итерации циклаcur
?2. Нет, это не так.
auto amp;
является ссылкой на другой объект. Не копия другого объекта.auto cur
является полноценным объектом.auto amp;cur
является ссылкой на какой-либо другой объект. Как вы думаете, в чем разница междуauto cur
иauto amp;cur
?auto amp;cur=q.top()
является ссылкой на объект в верхней части очереди.pop()
уничтожает этот объект. Этого больше нет. Он перестал существовать. Он тоскует по фьордам. Это бывший объект. Тот факт, что где-то была ссылка на объект, не мешает его уничтожению.3. хорошо, я думаю, я понимаю. Но даже тогда я подумал, что, поскольку я переназначаю ссылку в
cur
top
на каждой итерации, она должна работать без создания копии пары. Как мне включить фактический объектtop
cur
без копирования?4.@pranav3688 Просто распечатайте
cur
перед выполнениемpop
. Ссылка будет действительна до тех пор.5. @SamVarshavchik: Я пытался использовать Valgrind в этом коде. Это не улавливается. Но спасибо!
Ответ №2:
Когда вы делаете:
autoamp; cur = q.top();
вы берете ссылку на верхний элемент в priority_queue
. Но когда вы делаете:
q.pop();
этот элемент удаляется, и все ссылки на него, в том числе cur
, остаются висячими. Любая попытка чтения, cur
например:
cout << cur.first << " " << cur.second << " ";
вызовет неопределенное поведение, которое может делать все, что угодно, включая выдачу результатов, которые вы наблюдали.
Выполнение:
auto cur = q.top();
это нормально, потому что вы создаете копию верхнего элемента. Удаление этого элемента из q
не влияет на копию.