0

When storing objects in standard collections, what considerations should one think of when deciding between storing values vs pointers? Where the owner of these collections (ObjectOwner) is allocated on the heap. For small structs/objects I've been storing values, while for large objects I've been storing pointers. My reasoning for this was that when standard containers are resized, their contents are copied (small copy ok, big copy bad). Any other things to keep in mind here?

class ObjectOwner
{
   public:
     SmallObject& getSmallObject(int smallObjectId);
     HugeObject* getHugeObject(int hugeObjectId);

   private:
     std::map<int, SmallObject> mSmallObjectMap; 
     std::map<int, HugeObject *> mHugeObjectMap; 
};

Edit:

an example of the above for more context:

  • Create/Delete items stored in std::map relatively infrequently ( a few times per second)
  • Get from std::map frequently (once per 10 milliseconds)
  • small object: < 32 bytes
  • huge object: > 1024 bytes
RandomUser
  • 3,812
  • 16
  • 49
  • 88
  • 1
    "My reasoning for this was that when standard containers are resized, their contents are copied" - depends on the container. No requirement for this at all; infact it's false for most containers – UKMonkey Nov 02 '16 at 14:58
  • "Any other things to keep in mind here?" Use smart pointers rather than raw if you can – UKMonkey Nov 02 '16 at 14:59
  • @UKMonkey It is true that resizing is not an issue in most containers but destruction and construction is, and that is expensive on large objects. – imreal Nov 02 '16 at 15:03
  • @imreal you have to construct and destruct the objects anyway, don't you ? – Quentin Nov 02 '16 at 15:05
  • @imreal As said by UKMonkey, a solution is using smart pointers: `shared_ptr` which allows sharing property of object, or `unique_ptr` for exclusive property – Garf365 Nov 02 '16 at 15:05
  • related/dupe: http://stackoverflow.com/questions/141337/c-stl-should-i-store-entire-objects-or-pointers-to-objects – NathanOliver Nov 02 '16 at 15:06
  • @Quentin you don't know how many times, the question is vague enough to make any assumptions to how the OP intends to use the containers. – imreal Nov 02 '16 at 15:06
  • @imreal If you use a container that doesn't reallocate, you're guaranteed a single construction and destruction. – Quentin Nov 02 '16 at 15:10
  • @Quentin Erasing and inserting in a vector, moving elements around, popping and pushing again come to mind. None of it would be an issue if the objects are movable though. – imreal Nov 02 '16 at 15:12
  • @imreal I did specify "that don't reallocate", which `std::vector` does but OP's `std::map`s don't. – Quentin Nov 02 '16 at 15:12
  • @Quentin the question seems to be about containers in general. But even in a map inserting an element involves a copy (if not using `emplace`). And so does making a copy of the whole map. And so does moving the element from one position to another. – imreal Nov 02 '16 at 15:17
  • Sorry all, I realize this is a vague question, just looking for general guidance and considerations that I might not be considering currently. Given the case of std::map, as I use that most often, I've added some more detail – RandomUser Nov 02 '16 at 15:17
  • @Hoofamon One more observation, someone suggested using `shared_ptr`, if your container won't share ownership, `unique_ptr` is the right tool to use, `shared_ptr` is quite slow. – imreal Nov 02 '16 at 15:24

2 Answers2

2

I would store object by value unless I need it through pointer. Possible reasons:

  • I need to store object hierarchy in a container (to avoid slicing)
  • I need shared ownership

There are possibly other reasons, but reasoning by size is only valid for some containers (std::vector for example) and even there you can make object moving cost minimal (reserve enough room in advance for example). You example for object size with std::map does not make any sense as std::map does not relocate objects when growing.

Note: return type of a method should not reflect the way you store it in a container, but rather it should be based on method semantics, ie what you would do if object is not found.

Slava
  • 40,641
  • 1
  • 38
  • 81
  • Could you explain further (as a **Note** for example), what is slicing about in programming? – Peregring-lk Nov 02 '16 at 15:33
  • @Peregring-lk it is fully explained here http://stackoverflow.com/questions/274626/what-is-object-slicing – Slava Nov 02 '16 at 15:34
  • 1
    Also std::vector has [a constant amortized time](http://stackoverflow.com/a/5232342/492336) for push_back because it grows exponentially, so even without `reserve()` performance would be OK. – sashoalm Nov 02 '16 at 15:35
  • @Slava ah ok, thanks. I didn't know object slicing was its english name. – Peregring-lk Nov 02 '16 at 15:48
0

Only your profiler knows the answer for your SPECIFIC case; trying to use pointers rather than objects is a reliable way of minimising the amount you copy when a copy must be done (be it a resize of a vector or a copy of the whole container); but sometimes you WANT that copy because it's a snapshot for a thread inside a mutex, and it's faster to copy the container than to hold the mutex and deal with the data.

Some objects might not be possible to keep in any way other than pointer because they're not copyable.

Any performance gain by using container of pointer could be offset by costs of having to write more copy code, or repeated calls to new().

There's not a one answer fits all, and before you worry about the performance here you should establish where the performance problems really are. (Just repeating the point - use a profiler!)

UKMonkey
  • 6,473
  • 3
  • 17
  • 29