Должен ли я хранить целые объекты или указатели на объекты в контейнерах? - PullRequest
160 голосов
/ 26 сентября 2008

Проектирование новой системы с нуля. Я буду использовать STL для хранения списков и карт некоторых долгоживущих объектов.

Вопрос: Должен ли я обеспечить, чтобы у моих объектов были конструкторы копирования и хранить копии объектов в моих контейнерах STL, или лучше вообще самостоятельно управлять жизнью и областью действия и просто хранить указатели на эти объекты в моих контейнерах STL? *

Я понимаю, что это несколько не хватает деталей, но я ищу "теоретический" лучший ответ, если он существует, поскольку я знаю, что оба эти решения возможны.

Два очень очевидных недостатка в игре с указателями: 1) Я должен сам управлять распределением / освобождением этих объектов за пределами STL. 2) Я не могу создать временный объект в стеке и добавить его в свои контейнеры.

Что-то еще мне не хватает?

Ответы [ 10 ]

67 голосов
/ 26 сентября 2008

Поскольку люди говорят об эффективности использования указателей.

Если вы планируете использовать std :: vector, и если обновлений немного, и вы часто выполняете итерацию по своей коллекции, и это не полиморфный тип, то хранение «копий» объекта будет более эффективным, поскольку вы получите лучшую локальность ссылок ,

Otoh, если обновления являются распространенными указателями хранения, сэкономит затраты на копирование / перемещение.

46 голосов
/ 26 сентября 2008

Это действительно зависит от вашей ситуации.

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

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

Если вы решили использовать указатели на объекты, взгляните на Boost Pointer Container Library . Эта библиотека повышения оборачивает все контейнеры STL для использования с динамически размещаемыми объектами.

Каждый контейнер указателя (например, ptr_vector) становится владельцем объекта при его добавлении в контейнер и управляет временем жизни этих объектов для вас. Вы также получаете доступ ко всем элементам в контейнере ptr_ по ссылке. Это позволяет вам делать такие вещи, как

class BigExpensive { ... }

// create a pointer vector
ptr_vector<BigExpensive> bigVector;
bigVector.push_back( new BigExpensive( "Lexus", 57700 ) );
bigVector.push_back( new BigExpensive( "House", 15000000 );

// get a reference to the first element
MyClass& expensiveItem = bigList[0];
expensiveItem.sell();

Эти классы обертывают контейнеры STL и работают со всеми алгоритмами STL, что очень удобно.

Существуют также средства для передачи владения указателем в контейнере вызывающей стороне (с помощью функции освобождения в большинстве контейнеров).

38 голосов
/ 26 сентября 2008

Если вы храните полиморфные объекты, вам всегда нужно использовать набор указателей базового класса.

То есть, если вы планируете хранить различные производные типы в своей коллекции, вы должны хранить указатели или быть съеденными срезанным демоном.

22 голосов
/ 26 января 2012

Извините, что прыгнул через 3 года после события, но предостережение здесь ...

В моем последнем большом проекте моей центральной структурой данных был набор довольно простых объектов. Примерно через год после того, как требования стали развиваться, я понял, что объект действительно должен быть полиморфным. Потребовалось несколько недель сложной и неприятной операции на головном мозге, чтобы исправить структуру данных как набор указателей базового класса и справиться со всеми сопутствующими повреждениями при хранении, приведении объектов и т. Д. Мне потребовалось несколько месяцев, чтобы убедить себя, что новый код работает. Кстати, это заставило меня задуматься о том, насколько хорошо разработана объектная модель C ++.

В моем текущем большом проекте моя центральная структура данных представляет собой набор довольно простых объектов. Примерно через год в проекте (который происходит сегодня) я понял, что объект на самом деле должен быть полиморфным. Обратно в сеть, нашел эту ветку и нашел ссылку Ника на библиотеку контейнера указателя Boost. Это именно то, что я должен был написать в прошлый раз, чтобы все исправить, поэтому на этот раз я попробую.

Мораль, для меня, так или иначе: если ваша спецификация не на 100% брошена в камне, используйте указатели, и вы потенциально можете сэкономить много работы позже.

18 голосов
/ 26 сентября 2008

Почему бы не получить лучшее из обоих миров: сделайте контейнер умных указателей (например, boost::shared_ptr или std::shared_ptr). Вам не нужно управлять памятью и вам не приходится иметь дело с большими операциями копирования.

11 голосов
/ 26 сентября 2008

Как правило, хранение объектов непосредственно в контейнере STL является наилучшим, поскольку оно является самым простым, наиболее эффективным и самым простым для использования объекта.

Если ваш объект имеет не копируемый синтаксис или является абстрактным базовым типом, вам нужно хранить указатели (проще всего использовать shared_ptr)

3 голосов
/ 26 сентября 2008

Использование указателей будет более эффективным, поскольку контейнеры будут копировать только указатели вместо полных объектов.

Здесь есть некоторая полезная информация о контейнерах STL и умных указателях:

Почему неправильно использовать std :: auto_ptr <> со стандартными контейнерами?

3 голосов
/ 26 сентября 2008

Вы, кажется, хорошо понимаете разницу. Если объекты маленькие и их легко копировать, то обязательно сохраните их.

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

@ Torbjörn хорошо подходит для нарезки ломтиками.

2 голосов
/ 27 сентября 2008

Если на объекты нужно ссылаться в другом месте кода, сохраняйте их в векторе boost :: shared_ptr. Это гарантирует, что указатели на объект останутся действительными, если вы измените размер вектора.

Т.е.:

std::vector<boost::shared_ptr<protocol> > protocols;
...
connection c(protocols[0].get()); // pointer to protocol stays valid even if resized

Если никто не хранит указатели на объекты или список не увеличивается и не уменьшается, просто сохраните как обычные старые объекты:

std::vector<protocol> protocols;
connection c(protocols[0]); // value-semantics, takes a copy of the protocol
1 голос
/ 19 июля 2013

Этот вопрос меня давно беспокоит.

Я склонен хранить указатели, но у меня есть некоторые дополнительные требования (SWIG lua wrappers), которые могут к вам не относиться.

Самый важный момент в этом посте - проверить его самостоятельно , используя ваши объекты

Я сделал это сегодня, чтобы проверить скорость вызова функции-члена в коллекции из 10 миллионов объектов, в 500 раз.

Функция обновляет x и y на основе xdir и ydir (все переменные-члены с плавающей точкой).

Я использовал std :: list для хранения обоих типов объектов и обнаружил, что сохранение объекта в списке немного быстрее, чем при использовании указателя. С другой стороны, производительность была очень близка, поэтому все сводится к тому, как они будут использоваться в вашем приложении.

Для справки: при использовании -O3 на моем оборудовании указатели выполнялись в течение 41 секунды, а необработанные объекты - в течение 30 секунд.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...