Когда я должен использовать вектор объектов вместо вектора указателей?
У меня есть коллекция полиморфных объектов, все они получены из моего класса Animal: Cat, Dog и MonkeyFish.
Мой обычный режим работы - хранить эти объекты в векторе указателей Animal, например:
std:: 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 - моя единственная альтернатива.
Когда я должен выбрать использование вектора указателей против вектора объектов? В общем, какой метод предпочтительнее? (Я бы хотел максимально сократить копирование объектов.)
4 ответа
Вы должны использовать вектор объектов, когда это возможно; но в вашем случае это невозможно.
Контейнеры указателей позволяют избежать проблемы срезов. Но тогда вы должны вызвать delete для каждого элемента, как вы делаете. Это раздражает, но возможно. К сожалению, бывают случаи (когда выдается исключение), когда вы не можете быть уверены, что удаление вызвано правильно, и в результате возникает утечка памяти.
Основным решением является использование умного указателя. Pre-C++11 поставляется с auto_ptr
, но это не может быть использовано в стандартном контейнере. C++11 имеет std::unique_ptr
а также std::shared_ptr
которые предназначены для использования в контейнерах (я предпочитаю std::unique_ptr
если только мне действительно не нужен подсчет ссылок). Если вы не можете использовать C++11, лучшее решение - повысить умные указатели.
Вместо того, чтобы использовать shared_ptr со стандартными контейнерами STL, взгляните на библиотеку контейнеров Boost Pointer. Он призван решить именно эту проблему.
В этом случае, сохраняя вектор Animal
не будет работать для вас, так как ваши животные имеют разные размеры, и вы не сможете хранить производные объекты в пространствах, предназначенных для хранения базового класса. (И даже если они имеют одинаковый размер, вы не получите желаемого полиморфного эффекта, так как будут выполняться методы базового класса - виртуальность метода не вступит в игру, если вы не получите к нему доступ через указатель или ссылку.)
Если вы хотите избежать раздражения, связанного с управлением памятью самостоятельно, вы можете подумать о том, чтобы сохранить умный указатель, такой как shared_ptr (обратите внимание, что auto_ptr не работает с контейнерами STL, по мнению Макса Либберта), или какой-либо его вариант. Таким образом, вы все еще можете использовать свой полиморфный класс, но для вас это немного меньше.
Нет никаких действительно жестких и быстрых правил о том, когда использовать объекты и указатели, хотя стоит отметить, что в некоторых случаях, например, у вас, объекты просто не будут работать для вас. Я склонен использовать объекты всякий раз, когда ничто не препятствует этому, хотя, как вы заметили, вы должны быть обеспокоены дорогостоящими операциями копирования (хотя иногда они могут быть улучшены путем передачи контейнеров по ссылке).
Если вы когда-нибудь услышите аргумент, но копировать их структуры будет все время, когда вы захотите использовать полные объекты вместо указателей в векторе, тогда ваши 2 основных аргумента:
- Нам не нужно беспокоиться о проблемах времени жизни указателей, что означает отсутствие утечек из этого конкретного кода (если, конечно, сами структуры не имеют данных указателей, но это уже другая история).
- Локальная структура данных смежных структур в памяти повысит производительность в типичных сценариях использования, а не замедлит работу, как косвенное указание (условно говоря).
Дополнительная стоимость копирования обычно берется при добавлении материала в контейнер, а не при использовании данных - подумайте немного об этом: что вы делаете больше всего? добавить предметы или использовать их?
Однако при добавлении полиморфных объектов указатели необходимы, чтобы избежать нарезки.