163

Designing a new system from scratch. I'll be using the STL to store lists and maps of certain long-live objects.

Question: Should I ensure my objects have copy constructors and store copies of objects within my STL containers, or is it generally better to manage the life & scope myself and just store the pointers to those objects in my STL containers?

I realize this is somewhat short on details, but I'm looking for the "theoretical" better answer if it exists, since I know both of these solutions are possible.

Two very obvious disadvantage to playing with pointers: 1) I must manage allocation/deallocation of these objects myself in a scope beyond the STL. 2) I cannot create a temp object on the stack and add it to my containers.

Is there anything else I'm missing?

Rakete1111
  • 42,521
  • 11
  • 108
  • 141
Stéphane
  • 17,613
  • 22
  • 82
  • 117
  • 36
    god i love this site, this is the EXACT question i was thinking of today...thanks for doing the work of asking it for me :-) – eviljack Jan 30 '09 at 14:19
  • 2
    another interesting thing is that we should check if the pointer was actually added to the collection and if it doesn't we likely should call delete to avoid memory leaks... if ((set.insert(pointer)).second = false) {delete pointer;} – Oleg Vazhnev Mar 11 '11 at 19:15

10 Answers10

68

Since people are chiming in on the efficency of using pointers.

If you're considering using a std::vector and if updates are few and you often iterate over your collection and it's a non polymorphic type storing object "copies" will be more efficent since you'll get better locality of reference.

Otoh, if updates are common storing pointers will save the copy/relocation costs.

Torbjörn Gyllebring
  • 16,878
  • 2
  • 27
  • 22
  • 8
    In terms of cache locality, storing pointers into vector can be efficient if used along with a custom allocator for the pointees. The custom allocator has to take care of the cache locality, for example using placement new (see http://en.wikipedia.org/wiki/Placement_syntax#Custom_allocators). – amit Feb 27 '11 at 11:34
47

This really depends upon your situation.

If your objects are small, and doing a copy of the object is lightweight, then storing the data inside an stl container is straightforward and easier to manage in my opinion because you don't have to worry about lifetime management.

If you objects are large, and having a default constructor doesn't make sense, or copies of objects are expensive, then storing with pointers is probably the way to go.

If you decide to use pointers to objects, take a look at the Boost Pointer Container Library. This boost library wraps all the STL containers for use with dynamically allocated objects.

Each pointer container (for example ptr_vector) takes ownership of an object when it is added to the container, and manages the lifetime of those objects for you. You also access all the elements in a ptr_ container by reference. This lets you do things like

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();

These classes wrap the STL containers and work with all of the STL algorithms, which is really handy.

There are also facilities for transferring ownership of a pointer in the container to the caller (via the release function in most of the containers).

Nick Haddad
  • 8,205
  • 3
  • 31
  • 38
39

If you're storing polymporhic objects you always need to use a collection of base class pointers.

That is if you plan on storing different derived types in your collection you must store pointers or get eaten by the slicing deamon.

philant
  • 31,604
  • 11
  • 64
  • 107
Torbjörn Gyllebring
  • 16,878
  • 2
  • 27
  • 22
22

Sorry to jump in 3 years after the event, but a cautionary note here...

On my last big project, my central data structure was a set of fairly straightforward objects. About a year into the project, as the requirements evolved, I realised that the object actually needed to be polymorphic. It took a few weeks of difficult and nasty brain surgery to fix the data structure to be a set of base class pointers, and to handle all the collateral damage in object storage, casting, and so on. It took me a couple of months to convince myself that the new code was working. Incidentally, this made me think hard about how well-designed C++'s object model is.

On my current big project, my central data structure is a set of fairly straightforward objects. About a year into the project (which happens to be today), I realised that the object actually needs to be polymorphic. Back to the net, found this thread, and found Nick's link to the the Boost pointer container library. This is exactly what I had to write last time to fix everything, so I'll give it a go this time around.

The moral, for me, anyway: if your spec isn't 100% cast in stone, go for pointers, and you may potentially save yourself a lot of work later.

EML
  • 965
  • 2
  • 8
  • 16
  • Specs are never set in stone. I don't think that means you should use use pointer containers exclusively, although Boost pointer containers seem to make that option much more attractive. I'm skeptical that you need to overhaul your entire program all at once if you decide that an object container should be converted to a pointer container. This might be the case under some designs. In that case, it is a fragile design. In that case, don't blame your problem on "weakness" of object containers. – allyourcode May 25 '13 at 02:22
  • You could have left the item in the vector with value semantics and did the polymorphic behavior inside. – Billy ONeal Jul 19 '13 at 01:42
19

Why not get the best of both worlds: do a container of smart pointers (such as boost::shared_ptr or std::shared_ptr). You don't have to manage the memory, and you don't have to deal with large copy operations.

Branan
  • 1,795
  • 14
  • 21
11

Generally storing the objects directly in the STL container is best as it is simplest, most efficient, and is easiest for using the object.

If your object itself has non-copyable syntax or is an abstract base type you will need to store pointers (easiest is to use shared_ptr)

Greg Rogers
  • 33,366
  • 15
  • 63
  • 93
3

You seem to have a good grasp of the difference. If the objects are small and easy to copy, then by all means store them.

If not, I would think about storing smart pointers (not auto_ptr, a ref counting smart pointer) to ones you allocate on the heap. Obviously, if you opt for smart pointers, then you can't store temp stack allocated objects (as you have said).

@Torbjörn makes a good point about slicing.

Community
  • 1
  • 1
Lou Franco
  • 83,503
  • 14
  • 127
  • 183
3

Using pointers will be more efficient since the containers will be only copying pointers around instead of full objects.

There's some useful information here about STL containers and smart pointers:

Why is it wrong to use std::auto_ptr<> with standard containers?

Community
  • 1
  • 1
17 of 26
  • 26,201
  • 13
  • 63
  • 84
2

If the objects are to be referred to elsewhere in the code, store in a vector of boost::shared_ptr. This ensures that pointers to the object will remain valid if you resize the vector.

Ie:

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

If noone else stores pointers to the objects, or the list doesn't grow and shrink, just store as plain-old objects:

std::vector<protocol> protocols;
connection c(protocols[0]); // value-semantics, takes a copy of the protocol
1

This question has been bugging me for a while.

I lean to storing pointers, but I have some additional requirements (SWIG lua wrappers) that might not apply to you.

The most important point in this post is to test it yourself, using your objects

I did this today to test the speed of calling a member function on a collection of 10 million objects, 500 times.

The function updates x and y based on xdir and ydir (all float member variables).

I used a std::list to hold both types of objects, and I found that storing the object in the list is slightly faster than using a pointer. On the other hand, the performance was very close, so it comes down to how they will be used in your application.

For reference, with -O3 on my hardware the pointers took 41 seconds to complete and the raw objects took 30 seconds to complete.

Meleneth
  • 296
  • 1
  • 6