C++ std::queue::pop() вызывает деструктор. Какие типы указателей?

У меня есть std::queue, который обернут как шаблонный класс, чтобы создать потокобезопасную очередь. У меня есть две версии этого класса: одна хранит типы значений, другая хранит типы указателей.

Для типа указателя у меня возникают проблемы с удалением элементов очереди при уничтожении. Причина в том, что я не знаю, как безопасно удалить элементы из очереди.

В этой ссылке говорится (бессмысленно, поэтому я думаю, что на самом деле это не ЗАЯВЛЯЕТСЯ), что единственный способ удалить элементы из очереди — это вызвать pop(). В ссылке также говорится, что pop() вызывает деструктор элемента.

Ну, это вызывает проблемы с моими типами указателей, потому что они могут фактически указывать или не указывать на агрегаты. Если один из них указывает на агрегат, все они будут, но, поскольку оболочка является шаблонной, нет гарантии, с каким типом (агрегированным или неагрегированным) мы имеем дело.

Итак, что происходит, когда pop() вызывает деструктор? Как убедиться, что все удаляется и память освобождается правильно?

Наконец, мое решение использует более старую версию GCC для ARM9. У меня нет контроля над этим. Я понимаю, что есть библиотеки, в которых есть интеллектуальные указатели и контейнеры, которые могли бы здесь помочь, но они для меня запрещены.


person San Jacinto    schedule 04.01.2010    source источник
comment
Ну, это вызывает проблемы с моими типами указателей, потому что они могут фактически указывать или не указывать на агрегаты. Я не понимаю, при чем здесь агрегаты.   -  person Lightness Races in Orbit    schedule 22.05.2011


Ответы (3)


Интернет-источники стоят столько, сколько вы за них платите — получите соответствующую ссылку, например книгу Джосуттиса. pop() не «вызывает деструктор» — он просто удаляет элемент из базового представления адаптера очереди (по умолчанию это std::deque), вызывая для него pop_front(). Если извлекаемая вещь имеет деструктор, он будет использоваться, когда извлекаемый объект выходит за пределы области видимости, но класс очереди не имеет к этому никакого отношения.

person Community    schedule 04.01.2010
comment
Спасибо за уточнение, у меня возникли проблемы с обдумыванием этого утверждения: это вызывает деструктор удаленного элемента - person Emile Vrijdags; 05.01.2010
comment
предполагая, что выскочивший объект был вставлен в интеллектуальный указатель какой-либо формы. - person Hassan Syed; 05.01.2010
comment
Спасибо за информацию и ссылку на книгу. - person San Jacinto; 05.01.2010
comment
Ничего не предполагая. Если у объекта есть деструктор, он будет вызван (но не методом pop()), если нет, то деструктор вызывать не нужно. - person ; 05.01.2010
comment
деструкторы автоматически вызываются только для элементов - когда они выходят за рамки - если у них есть умные указатели..... я что-то здесь упустил...? - person Hassan Syed; 05.01.2010
comment
Действительно ли справедливо сказать, что не с помощью pop()? Это все равно, что сказать, что main ничего не делает, потому что main только вызывает другие функции. Но главное делает все. - person GManNickG; 05.01.2010
comment
Да, я не думаю, что вы понимаете, как работают умные указатели. Но это слишком большая тема для ТАКОГО комментария. - person ; 05.01.2010
comment
@GMan Достаточно честно. Но вызов деструктора обычно зарезервирован на языке C++ для тех случаев, когда вы действительно вызываете его, например, когда вы используете новое размещение. В этом случае все, что делает pop, — это удаляет элемент из двухсторонней очереди. - person ; 05.01.2010
comment
ВТФ? , объект может быть создан заново или выделен в стеке, первый не будет автоматически выпущен в OOS без умного указателя - ваша совокупность не держится. - person Hassan Syed; 05.01.2010
comment
и парень говорит как об указателях, так и о типах значений... в контексте С++. Я бы сказал, справедливо предположить, что они могут быть новыми. - person Hassan Syed; 05.01.2010
comment
@Hassan Все в порядке. Пожалуйста, перечитайте то, что я написал. - person ; 05.01.2010
comment
@Neil В своей программе я вызываю pop(). Представим, что следующая строка в моем коде — это i++; Был ли вызван деструктор к моменту возникновения i++? Я не спрашиваю, вызвала ли функция pop() деструктор, я спрашиваю, была ли вызвана функция pop(), если она была удалена. - person San Jacinto; 05.01.2010
comment
@Hassan: контейнеры всегда, всегда уничтожают содержащиеся в них объекты, когда они удаляются из контейнера. Всегда. Не имеет значения, является ли содержащийся объект определяемым пользователем типом, целым числом, указателем или интеллектуальным указателем. Он уничтожается (или ничего не происходит, если у него нет деструктора). Тогда единственный вопрос заключается в том, что на самом деле делает уничтожение всплывающего объекта. Для указателя он ничего не делает. Объект newd никогда не может быть содержимым контейнера, независимо от того, вставлен ли он в интеллектуальный указатель или нет. Указатель на него может быть содержимым контейнера, но это совсем другое. - person Steve Jessop; 05.01.2010
comment
@GMan Просто чтобы уточнить, есть ли у меня очередь строк и я выполняю d.pop_front(), вызываю ли я деструктор для строки, которую я извлекаю? Ну да, но большинство программистов на C++ так не думают. Я думаю :-) - person ; 05.01.2010
comment
Чтобы перефразировать комментарии Нилса. Когда вы извлекаете объект (из стандартного контейнера), деструктор будет вызываться для извлеченного объекта. Объект-указатель НЕ имеет деструктора (то, на что он указывает, может иметь деструктор, но это не то, что хранится в очереди). Таким образом, объект-указатель будет просто удален из очереди. - person Martin York; 05.01.2010
comment
Извлеченный объект может быть копией, используемой в очереди, или возвращенной копией. В любом случае только интеллектуальный указатель boost исправит вашу совокупность. - person Hassan Syed; 05.01.2010
comment
@Сан-Хасинто Хороший вопрос! Я предлагаю начать еще один вопрос и опубликовать точный код хотя бы той функции, о которой вы спрашиваете. Иначе однозначного ответа нет. - person ; 05.01.2010
comment
@steve, вы всегда можете разыменовать указатель new, чтобы поместить элемент в очередь: / и это обычная идиома: D - person Hassan Syed; 05.01.2010
comment
@Martin Предполагая, что вы разговариваете со мной, исправлением тупого и неясного ответа является публикация четкого и прозрачного ответа. - person ; 05.01.2010
comment
@Мартин, хотел бы я изменить на (-1) - person Hassan Syed; 05.01.2010
comment
Мне так и хочется начать кричать что-то вроде НЕ ПАНИКУЙ, МИСТЕР МЕЙНВАРИНГ! На самом деле, здесь три (?) проблемы, и все они должны быть рассмотрены в отдельных вопросах. - person ; 05.01.2010
comment
Спасибо, Нил и Стив. Ваши комментарии (и ответ) в совокупности прояснили это для меня. - person San Jacinto; 05.01.2010
comment
@Hassan: это не ставит объект в очередь, а ставит в очередь его копию. - person Steve Jessop; 05.01.2010
comment
@Hassan: что вы имеете в виду? Выскочивший объект может быть копией, используемой в очереди, или возвращенной копией? pop() ничего не возвращает. - person Steve Jessop; 05.01.2010
comment
@Neil: я только что понял, что это дело о том, что называется, что, вероятно, является причиной того, что все (включая меня) заканчивают тем, что пишут всю свою документацию в пассивном залоге, и получается эквивалент Флеша-Кинкейда старше Мафусаила. . объект удаляется из базового контейнера до возврата pop(), в результате чего его деструктор (если есть) вызывается до возврата pop(), а pop() удаляет объект из базового контейнера, следовательно, вызывает его деструктор (если Любые). - person Steve Jessop; 05.01.2010
comment
@Steve, я не вижу, что не так с удалением объекта из базового контейнера - мысль о воспроизведении объяснения семантики деструктора в каждом случае пугает. И на самом деле именно так написан стандарт C++. - person ; 05.01.2010
comment
@Martin ТАК не принято голосовать за людей за их правильность, независимо от того, насколько сложно вы можете найти их ответы. - person ; 05.01.2010

Указатели сами на самом деле не имеют деструкторов, поэтому вызов pop() для очереди, содержащей указатель, не вызовет деструктор объекта, на который указывает ваш указатель.

person Greg Hewgill    schedule 04.01.2010
comment
Спасибо за ответ. Это то, что мне было интересно, если это не было ясно из моего бессвязного вопроса. - person San Jacinto; 05.01.2010
comment
Грег, я ценю быстрый ответ и все еще голосую, но я изменил свое согласие на ответ Нила, потому что в нем есть немного более подробное объяснение того, что происходит. - person San Jacinto; 05.01.2010
comment
Я согласен. Хотя ход мыслей очень хороший :D. Первым сигнальным признаком должно быть то, что указатель не имеет деструктора. - person Hassan Syed; 05.01.2010
comment
@Hassan нет, у него нет деструктора, но компилятор может отслеживать типы и, следовательно, может вызывать удаление, если мы говорим об указывающем типе. Я не думал, что это вероятно, но то, что происходит НА САМОМ ДЕЛЕ, имеет значение. - person San Jacinto; 05.01.2010
comment
Компиляторы вызывают T::~T(), когда вы уничтожаете умный указатель на T, например. std::auto_ptr‹T› или std::tr1::shared_ptr<T>. Это в значительной степени определение умных указателей. Он не делает таких вещей для необработанных указателей, хотя бы для обеспечения совместимости с C. - person MSalters; 05.01.2010

«Как мне убедиться, что все удаляется и память освобождается правильно?»

Если вам абсолютно необходимо хранить указатели в своей очереди, и вы хотите, чтобы они автоматически освобождались, когда они poped, то вместо очереди указателей вам нужна очередь объектов, которые хранят указатель и удаляют его в своем деструкторе. . Например, вы можете использовать очередь shared_ptr. shared_ptr не входит в стандартную библиотеку, но является частью TR1 и широко доступен.

В противном случае ответственность за удаление объекта лежит на вызывающем объекте:

T *off = q.front();
q.pop();
delete off;

Подводя итог, можно сказать, что контейнеры указателей на динамически размещаемые объекты немного неудобны. Если вы можете спроектировать свою программу так, чтобы в контейнерах хранились копии ваших объектов, а не указатели на динамические объекты, сделайте это. В противном случае ответственность за владение ресурсами несете вы, а не контейнер. Контейнеры STL ничего не знают о собственности, они просто копируют и уничтожают свои value_type. Копирование и уничтожение указателей ничего не делает с объектами, на которые они указывают.

person Steve Jessop    schedule 04.01.2010
comment
Я думал об этом, но отверг его, потому что я не знаю будущего использования шаблонного класса. Я не хочу предоставлять необработанный доступ к очереди для пользователей из-за соображений безопасности потоков, и я не могу гарантировать, что пользователи воспользуются идеей, которую вы только что упомянули, поэтому я отказался от этого. - person San Jacinto; 05.01.2010
comment
Если ваш класс использует свой параметр шаблона в качестве типа значения для скрытой очереди, то он, вероятно, должен делать то, что делают контейнеры: если пользователь выделяет что-либо, то пользователь должен принять меры, чтобы в конечном итоге это освободить (например, с помощью интеллектуального указателя). как тип значения). Например, если это очередь событий, и пользователь настаивает на использовании простого указателя, то он, возможно, мог бы освободить объект в обработчике событий. Но это не проблема контейнера, и IMO вы не должны пытаться писать контейнеры, которые специально обрабатывают указатели. - person Steve Jessop; 05.01.2010
comment
... по общему признанию, это просто перекладывает проблему на следующего парня. Но есть веские причины не путать контейнеры с владением, не последней из которых является то, что кто-то может захотеть использовать ваш контейнер для хранения указателей, не предоставляя вам права собственности (потому что они указывают на статические данные или что-то в этом роде). Я предполагаю, что boost::ptr_deque можно использовать в качестве базового контейнера для очереди, поэтому вы можете иметь два параметра шаблона (например, очередь) и позволить пользователю использовать это, если он делает то, что хочет. - person Steve Jessop; 05.01.2010
comment
Обычно я согласен с этой здравой логикой, но для моего непосредственного использования удаление элементов происходит в аномальных ситуациях. В моем классе элементы удаляются только при вызове деструктора, а это означает, что для этой конкретной подсистемы, вероятно, произошел крупный сбой. По сути, я использую эту очередь в качестве посредника для производителя/потребителя, и если были произведены элементы, их необходимо правильно и сразу утилизировать, если произошла такая большая ошибка. В противном случае поток драйвера (потребителя) будет продолжать работать, и в аппаратном плане дела пойдут плохо. - person San Jacinto; 05.01.2010