Удаление из STL std :: queue без разрушения удаленного объекта? - PullRequest
7 голосов
/ 03 октября 2009

Вся документация, которую я могу найти в контейнерах STL (и очередь, и список), говорит, что для любой из функций удаления вызывается деструктор удаленного объекта. Это означает, что я не могу использовать std :: queue в любое время, когда мне нужна очередь, которая представляет собой просто список объектов, для которых требуется выполнить некоторую операцию.

Я хочу иметь возможность добавлять объекты в очередь, когда они ждут своей очереди, чтобы я что-то с ними сделал. Затем я хочу удалить их из него, когда закончу с ними, не уничтожая рассматриваемый объект. Это невозможно из документации, которую я прочитал. Я неправильно читаю документацию? Есть ли другой тип очереди в STL, кроме базовой «очереди», которая не вызывает деструктор удаленного объекта при вызове pop_front?

Изменить, чтобы уточнить: В моем случае я использую список указателей. Как то так:

   dbObject *someObject;
   queue<dbObject *> inputQueue;
   inputQueue.push_back(someObject);

   ...

   dbObject *objectWithInput = inputQueue.front();
   //handle object's input...
   inputQueue.pop_front(); // Remove from queue... destroyed now?

Ответы [ 5 ]

17 голосов
/ 03 октября 2009

Если вы поместите указатели на объекты в очереди (и любой другой контейнер STL), указатели не будут удалены при их удалении.

Для уточнения: когда вы используете std :: queue и удаляете объект, вызывается деструктор some_obj *. Но деструктор для простого указателя (или любого типа POD - int, char и т. Д.) Пуст, нет операции. Тонкая грань здесь в том, что деструктор для some_obj * очень отличается от деструктора для some_obj.

7 голосов
/ 03 октября 2009

Контейнеры STL имеют семантику значений. Когда вы помещаете объект в контейнер STL, контейнер STL сохраняет свою собственную копию объекта, а когда объект (внутренняя копия) удаляется из контейнера, он уничтожается.

Если вы использовали контейнер прокси-типа, в качестве необработанных указателей, интеллектуальных указателей (shared_ptr, weak_ptr) или адаптеров (как boost :: reference_wrapper), тогда контейнер STL уничтожит прокси-сервер, но не этот тип. Выбор одного из других обычно зависит от того, как вы хотите иметь дело с ресурсами.

Наиболее распространенная идиома - использование необработанных указателей, но они не указывают, кто отвечает за уничтожение (код, извлекаемый из контейнера, должен удалить указатель, или ресурс обрабатывается где-то еще?).

Современное использование движется к подходу shared_ptr, поскольку это ослабляет проблему владения. Объект будет гарантированно живым, когда вы извлечете его из контейнера, и если никто не хранит shared_ptr, тогда объект будет автоматически удален, когда локальный shared_ptr выйдет из области видимости. Использование weak_ptr сохранит владение исходным кодом, но позволит вам проверить правильность указателя (если он был удален) перед использованием. Это может позволить вам избежать выполнения операции над объектом, который будет удален сразу.

Проблема с подходом shared_ptr / weak_ptr заключается в том, что он заставляет вас использовать shared_ptr для хранения исходного ресурса. Это означает, что вы не сможете поместить указатель в подобъект (атрибут member) другого класса, не изменив класс для хранения атрибута через shared_ptr, и это будет иметь другие последствия (атрибуты больше не будут смежными в памяти , потребуются более динамические операции выделения ...)

Техника, которая едва заметна, использует адаптеры как boost :: reference_wrapper <>. Оболочка ссылки - это прокси-объект, который содержит ссылку на исходный объект и сам является копируемым. Преимущество перед простыми необработанными указателями состоит в том, что при чтении кода становится очевидным, что ресурс управляется вне очереди: код, который извлекает данные из очереди, не должен удалять объект. Преимущество перед умным указателем заключается в том, что вам не нужно перепроектировать другие части вашей системы, чтобы использовать умные указатели. Недостаток заключается в том, что, как и в подходе с необработанным указателем, вы должны убедиться, что время жизни упомянутого объекта превосходит ссылку в контейнере вручную.

4 голосов
/ 03 октября 2009
class someobj_t {};

std::queue<someobj_t> q;
...

someobj_t ppd = q.front(); // ppd is not a reference
q.pop();

// ppd now contain removed object

Если вы не хотите копировать someobj_t, вы можете использовать std::queue< shared_ptr<someobj_t> >.

3 голосов
/ 03 октября 2009

Как насчет использования списка указателей на объекты?

2 голосов
/ 03 октября 2009

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

Хранение указателей в контейнерах имеет те же недостатки, что и размещение в локальном необработанном указателе, память не очищается автоматически. В функции, если вы не удалите указатель или не передадите владение, вернув его, у вас будет утечка памяти.

При хранении необработанных указателей в контейнере владение может стать немного двусмысленным, и утечки могут легко произойти. Взгляните на tr1 :: shared_ptr и сохраните их в контейнерах.

std :: unique_ptr в C ++ 0x также будет хорошим решением для хранения указателя в контейнере stdlib, когда он доступен.

...