Когда следует использовать вектор объектов вместо вектора указателей?
У меня есть набор полиморфных объектов, все из моего класса Animal: Cat, Dog и MonkeyFish.
Мой обычный режим работы - хранить эти объекты в векторе указателей на животных, например:
std::vector < Animal * > my_vector;
my_vector.push_back( new Animal_Cat() );
my_vector.push_back( new Animal_Dog() );
my_vector.push_back( new Animal_MonkeyFish() );
И жизнь велика... или это?
Недавно мне сказали, что я действительно должен стараться не выделять память таким образом, потому что это делает управление памятью сложной задачей. Когда мне нужно уничтожить my_vector, я должен выполнить итерацию по всем элементам и удалить все.
Я не думаю, что я могу хранить вектор ссылок (возможно, я ошибаюсь в этом), поэтому мне кажется, что сохранение вектора объектов Animal является моей единственной альтернативой.
Когда я должен использовать вектор указателей против вектора объектов? В общем, какой способ является предпочтительным? (Я хотел бы как можно больше уменьшить копирование объектов.)
Ответы
Ответ 1
Вы должны использовать вектор объектов, когда это возможно; но в вашем случае это невозможно.
Контейнеры указателей позволяют избежать проблемы нарезки. Но тогда вы должны называть delete для каждого элемента, как вы делаете. Это раздражает, но возможно. К сожалению, есть случаи (когда вызывается исключение), где вы не можете быть уверены, что удаление правильно вызвано, и вы закончите утечку памяти.
Основное решение - использовать интеллектуальный указатель. Pre-С++ 11 поставляется с auto_ptr
, но не может использоваться в стандартном контейнере. С++ 11 имеет std::unique_ptr
и std::shared_ptr
, которые предназначены для использования в контейнерах (я предпочитаю std::unique_ptr
, если мне действительно не нужен подсчет ссылок). Если вы не можете использовать С++ 11, лучшим решением является Повысить интеллектуальные указатели.
Ответ 2
В этом случае сохранение вектора Animal
не будет работать для вас, так как ваши животные имеют разные размеры, и вы не сможете хранить производные объекты в пространствах, предназначенных для хранения базового класса. (И даже если они имеют одинаковый размер, вы не получите предполагаемого полиморфного эффекта, поскольку будут выполняться методы базового класса - виртуальность метода не вступает в игру, если вы не получите доступ к нему через указатель или ссылку. )
Если вы хотите избежать раздражения управления памятью самостоятельно, вы можете рассмотреть возможность хранения интеллектуального указателя, такого как shared_ptr ( обратите внимание, что auto_ptr не работает с контейнерами STL, в соответствии с Max Lybbert) или с каким-либо вариантом. Таким образом, вы все равно можете использовать свой полиморфный класс, но для вас это немного меньше.
Там нет реальных жестких правил о том, когда использовать объекты и указатели, хотя стоит заметить, что в некоторых случаях, как и ваши, объекты просто не будут работать для вас. Я обычно использую объекты, когда ничто не исключает его, хотя вы должны быть обеспокоены дорогостоящими операциями копирования, как вы заметили (хотя иногда их можно улучшить, передав контейнеры по ссылке).
Ответ 3
Вместо использования shared_ptr со стандартными контейнерами STL, посмотрите Boost Pointer Container Library. Он предназначен для решения именно этой проблемы.
Ответ 4
Если вы когда-нибудь услышите аргумент, но будет так дорого копировать их структуры все время, когда вы хотите использовать полные объекты вместо указателей в векторе, то ваши 2 основных аргумента:
- Нам не нужно беспокоиться о жизненных проблемах указателей, что означает отсутствие утечек из этого конкретного кода (если, конечно, сами структуры не имеют данных указателя, но эта другая история).
- Локальность данных соседних структур в памяти повысит производительность в типичных сценариях использования, а не замедлит работу вниз, как указатель на указатель (относительно говоря).
Добавленная стоимость копирования обычно берется при добавлении материала в контейнер, а не при использовании данных - подумайте немного об этом: что вы делаете больше всего? добавлять элементы или использовать их?
Однако при добавлении полиморфных объектов указатели необходимы, чтобы избежать нарезки.