# C++: Smart pointers, Raw pointers, No Pointers? [closed]

Within the scope of developing games in C++, what are your preferred patterns in regard to use of pointers (be it none, raw, scoped, shared, or otherwise in between smart and dumb)?

You might consider

• object ownership
• ease of use
• copy policy
• cyclic references
• target platform
• use with containers

After having tried various approaches, today I find myself in alignment with the Google C++ Style Guide:

If you actually need pointer semantics, scoped_ptr is great. You should only use std::tr1::shared_ptr under very specific conditions, such as when objects need to be held by STL containers. You should never use auto_ptr. [...]

Generally speaking, we prefer that we design code with clear object ownership. The clearest object ownership is obtained by using an object directly as a field or local variable, without using pointers at all. [..]

Although they are not recommended, reference counted pointers are sometimes the simplest and most elegant way to solve a problem.

• Today, you might want to use std::unique_ptr instead of scoped_ptr. Dec 9, 2011 at 14:30

I also follow the "strong ownership" train of thought. I like to clearly delineate that "this class owns this member" when its appropriate.

I rarely use shared_ptr. If I do, I make liberal use of weak_ptr whenever I can so I can treat it like a handle to the object instead of increasing the reference count.

I use scoped_ptr all over the place. It shows obvious ownership. The only reason I don't just make objects like that a member is because you can forward declare them if they're in a scoped_ptr.

If I need a list of objects, I use ptr_vector. It's more efficient and has fewer side effects than using vector<shared_ptr>. I think you might not be able to forward declare the type in the ptr_vector (it's been a while), but the semantics of it make it worth it in my opinion. Basically if you remove an object from the list it gets deleted automatically. This also shows obvious ownership.

If I need reference to something, I try to make it a reference instead of a naked pointer. Sometimes this isn't practical (i.e. any time you need a reference after the object is constructed). Either way, references show obviously that you don't own the object, and if you're following shared pointer semantics everywhere else then naked pointers generally don't cause any additional confusion (especially if you follow a "no manual deletes" rule).

With this method, one iPhone game I worked on was able to only have a single delete call, and that was in the Obj-C to C++ bridge I wrote.

Generally I'm of the opinion that memory management is too important to leave to humans. If you can automate deletion, you should. If the overhead from shared_ptr is too expensive at run time (assuming you turned off threading support, etc), you probably should be using something else (i.e. a bucket pattern) to get your dynamic allocations down.

• Excellent summary. Do you actually mean shared_ptr as opposed to your mentioning of smart_ptr? Aug 11, 2010 at 16:36
• Yes, I meant shared_ptr. I'll fix that. Aug 11, 2010 at 17:35

Use the right tool for the job.

If your program can throw exceptions ensure your code is exception aware. Using smart pointers, RAII, and avoiding 2 phase construction are good starting points.

If you have cyclic references with no clear ownership semantics you can consider using a garbage collection library or refactoring your design.

Good libraries will allow you to code to the concept not the type so it shouldn't matter in most cases which kind of pointer you are using beyond resource management issues.

If you're working in a multi-threaded environment, make sure you understand if your object is potentially shared across threads. One of the main reasons to consider using boost::shared_ptr or std::tr1::shared_ptr is because it uses a thread-safe reference count.

If you're worried about the separate allocation of the reference counts there are many ways around this. Using the boost::shared_ptr library you can pool allocate the reference counters or use boost::make_shared (my preference) which allocates the object and the reference count in a single allocation thereby alleviating most cache miss concerns people have. You can avoid the performance hit of updating the reference count in performance critical code by holding a reference to the object at the topmost level and passing around direct references to the object.

If you need shared ownership but don't want to pay the cost of reference counting or garbage collection consider using immutable objects or a copy on write idiom.

Bear in mind that far and away your biggest performance wins are going to be at an architecture level, followed by an algorithm level, and while these low level concerns are very important they should be tackled only after you've addressed the major issues. If you're dealing with performance issues at the level of cache misses then you have a whole host of issues that you also have to be aware of like false sharing which have nothing to do with pointers per say.

If you're using smart pointers just to share resources like textures or models consider a more specialized library like Boost.Flyweight.

Once the new standard becomes adopted move semantics, rvalue references, and perfect forwarding will make working with expensive objects and containers much easier and more efficient. Until then don't store pointers with destructive copy semantics, such as auto_ptr or unique_ptr, in a Container (the standard concept). Consider using the Boost.Pointer Container library or storing shared ownership smart pointers in Containers. In performance critical code you can consider avoiding both of these in favor of intrusive containers such as those in Boost.Intrusive.

The target platform shouldn't really influence your decision too much. Embedded devices, smart phones, dumb phones, PCs, and consoles can all run the code just fine. Project requirements such as strict memory budgets or no dynamic allocation ever/after load are more valid concerns and should influence your choices.

• Exception handling on the consoles can be a bit dodgy -- the XDK in particular is sort of exception-hostile. Aug 11, 2010 at 21:32
• Target platform really should influence your design. The hardware that transforms your data can sometimes have large influences on your source-code. The PS3-architecture is a concrete example where you really need to take the hardware into designing your resource and memory-management as well as your renderer. Aug 12, 2010 at 16:38
• I disagree only slightly, specifically with regard to GC. Most of the time, cyclic references are not a problem for reference counted schemes. Generally these cyclic ownership problems come up because people didn't think properly about the ownership of objects. Just because an object needs to point to something, doesn't mean it should own that pointer. The commonly cited example is back pointers in trees, but the parent to the pointer in a tree can safely be a raw pointer without sacrificing safety. Sep 15, 2013 at 13:06

If you're using C++0x, use std::unique_ptr<T>.

It has no performance overhead, unlike std::shared_ptr<T> which has reference counting overhead. A unique_ptr owns its pointer, and you can transfer ownership around with C++0x's move semantics. You can't copy them - only move them.

It can also be used in containers, e.g. std::vector<std::unique_ptr<T>>, which is binary-compatible and identical in performance to std::vector<T*>, but will not leak memory if you erase elements or clear the vector. This also has better compatibility with STL algorithms than ptr_vector.

IMO for a lot of purposes this is an ideal container: random access, exception safe, prevents memory leaks, low overhead for vector reallocation (just shuffles around pointers behind the scenes). Very useful for many purposes.

It's good practice to document which classes owns what pointers. Preferably, you just use normal objects, and no pointers whenever you can.

However, when you need to keep track of resources, passing pointers is the only option. There are some cases:

• You get the pointer from somewhere else, but do not manage it: just use a normal pointer and document it so that no coder after you tries to delete it.
• You get the pointer from somewhere else, and you keep track of it: use a scoped_ptr.
• You get the pointer from somewhere else, and you keep track of it but it needs a special method to delete it: use shared_ptr with a custom delete method.
• You need the pointer in an STL container: it will be copied around so you need boost::shared_ptr.
• Many classes share the pointer, and it's not clear who will delete it: shared_ptr (the case above is actually a special case of this point).
• You create the pointer yourself and only you need it: if you really can't use a normal object: scoped_ptr.
• You create the pointer and will share it with other classes: shared_ptr.
• You create the pointer and pass it: use a normal pointer and document your interface so that the new owner knows that he should manage the resource himself!

I think that pretty much covers how I manage my resources right now. The memory cost of a pointer like shared_ptr is generally twice the memory cost of a normal pointer. I don't think that this overhead is too big, but if you are low on resources you should consider designing your game to reduce the number of smart pointers. On other cases I just design to good principles like the bullets above and the profiler will tell me where I will need more speed.

When it comes to boost's pointers specifically, I think that they should be avoided as long as their implementation is not exactly what you need. They do come at a cost that is larger than anyone would initially expect. They provide an interface that allows you to skip vital and important parts of your memory and resourcement management.

When it comes to any software development I think that it is important to think about your data. It is very important how your data is represented in memory. The reason for this is that CPU-speed has been increasing at a much greater rate than memory-access time. This often makes the memory-caches the main bottleneck of most modern computer games. By having your data aligned linearly in memory according to access order is much friendlier to the cache. This kind of solutions often lead to cleaner designs, simpler code and definetly code that is more easy to debug. Smart pointers easily lead to frequent dynamic memory allocations of resources, this causes them to be scattered all over the memory.

This is not a premature optimization, it's a healthy decision that can and should be taken as early as possible. It's a question of architectural understanding of the hardware that your software will run on and it is important.

Edit: There are a few things to consider regarding the performance of shared-pointers:

• The reference counter is heap allocated.
• If you use thread-safety enabled, reference counting is done via interlocked operations.
• Passing the pointer by value modifies reference count, which means interlocked operations most likely using random access in memory (locks + likely cache miss).
• You lost me at 'avoided at all costs.' Then you go on to describe a type of optimization that is seldom a concern for real-world games. Most game development is characterized by development problems (delays, bugs, playability, etc) not by a lack of CPU cache performance. So I strongly disagree with the idea that this advice isn't a premature optimization. Aug 11, 2010 at 15:39
• I have to agree with early design of data layout. It is important to get any performance out of a modern console / mobile device and is something that should never be over-looked.
– Olly
Aug 11, 2010 at 16:10
• This is an issue that I've seen at one of the AAA-studios that I've been working at. You can also listen to the Head Architect at Insomniac Games, Mike Acton. I'm not saying that boost is a bad library, it's not just well suited for high-performant games. Aug 12, 2010 at 5:47
• @kevin42: Cache coherency is probably the main source of low-level optimizations in game development today. @Simon: Most shared_ptr implementations avoid locks on any platform that supports compare-and-swap, which includes Linux and Windows PCs, and I believe includes the Xbox.
– user744
Aug 12, 2010 at 8:57
• @Joe Wreschnig: That's true, the cache-miss is still most likely though causing any initialization of a shared-pointer (copy, create from weak pointer etc). An L2 cache-miss on modern PC's is like 200 cycles and on the PPC (xbox360/ps3) it is higher. With an intense game you might have up to 1000 game objects, given that each game object can have quite a few resources we're looking at issues where their scaling is a major concern. This will likely cause issues at the end of a development cycle (when you will hit the high amount of game objects). Aug 12, 2010 at 13:07

I tend to use smart pointers everywhere. I am not sure if this is a totally good idea, but I am lazy, and I cannot see any real downside [except if I wanted to do some C-style pointer arithmetic]. I use boost::shared_ptr because I know I can copy it around - if two entities share an image, then if one dies the other shouldn't lose the image too.

The downside of this is if one object deletes something it points to and owns, but something else is also pointing to it, then it isn't deleted.

• I have been using share_ptr almost everywhere, too - but today I try to think whether or not I actually need shared ownership for some piece of data. If not, it might be reasonable to make that data a non-pointer member to the parent data structure. I find that clear ownership simplifies designs. Aug 11, 2010 at 11:24

The benefits of memory management and documentation provided by good smart pointers mean I use them regularly. However when the profiler pipes up and tells me a particuarly usage is costing me, I'll revert back to more neolithic pointer management.

I'm old, oldskool, and a cycle counter. In my own work I use raw pointers and no dynamic allocations at runtime (except the pools themselves). Everything is pooled, and ownership is very strict and never transferable, if really needed I write a custom small block allocator. I make sure that there is a state during game for every pool to clear itself. When things get hairyI do wrap objects in handles so I can relocate them, but I'd rather not. Containers are custom and extremely bare bones. I also don't reuse code.
While I would never argue the virtue of all the smart pointers and containers and iterators and whatnot, I am known for being able to code extremely fast (and reasonably reliable - although it's not advisable for others to jump into my code for somewhat obvious reasons, like heart-attacks and perpetual nightmares).

At work, of course, all is different, unless I am prototyping, which I thankfully get to do a lot.

Almost none though this is admittedly a strange answer, and probably nowhere close to suitable for everyone.

But I have found it so much more useful in my personal case to store all instances of a particular type in a central, random-access sequence (thread-safe), and instead to work with 32-bit indices (relative addresses, i.e.), rather than absolute pointers.

For a start:

1. It halves the memory requirements of the analogical pointer on 64-bit platforms. So far I've never needed more than ~4.29 billion instances of a particular data type.
2. It makes sure that all instances of a particular type, T, will never be too scattered in memory. That tends to reduce cache misses for all kinds of access patterns, even traversing linked structures like trees if the nodes are linked together using indices rather than pointers.
3. Parallel data becomes easy to associate using cheap parallel arrays (or sparse arrays) instead of trees or hash tables.
4. Set intersections can be found in linear-time or better using, say, a parallel bitset.
5. We can radix sort the indices and get a very cache-friendly sequential access pattern.
6. We can keep track of how many instances how of a particular data type have been allocated.
7. Minimizes the number of places that have to deal with things like exception-safety, if you care about that sort of thing.

That said, convenience is a downside as well as type safety. We can't access an instance of T without having access to both container and index. And a plain old int32_t tells us nothing about what data type it refers to, so there's no type safety. We could accidentally try to access a Bar using an index to Foo. To mitigate the second problem I often do this sort of thing:

struct FooIndex
{
int32_t index;
};


Which seems kind of silly but it gives me back the type safety so that people can't accidentally try to access a Bar through an index to Foo without a compiler error. For the convenience side, I just accept the slight inconvenience.

Another thing which could be a major inconvenience for people is that I can't use OOP-style inheritance-based polymorphism, since that would call for a base pointer which can point to all kinds of different subtypes with different size and alignment requirements. But I don't use inheritance much these days -- prefer the ECS approach.

As for shared_ptr, I try not to use it so much. Most of the time I don't find it makes sense to share ownership, and doing so haphazardly can lead to logical leaks. Often at least at a high-level, one thing tends to belong to one thing. Where I often found it tempting to use shared_ptr was extending the lifetime of an object in places that didn't really deal with ownership so much, like just a local function in a thread to make sure the object isn't destroyed before the thread is finished using it.

To tackle that problem, instead of using shared_ptr or GC or anything like that, I often favor short-lived tasks running from a thread pool, and make it so if that thread requests to destroy an object, that the actual destruction is deferred to a safe time when the system can ensure that no thread needs to access said object type.

I do still sometimes end up using ref-counting but treat it like a last resort strategy. And there are a few cases where it genuinely makes sense to share ownership, like the implementation of a persistent data structure, and there I do find it makes perfect sense to reach for shared_ptr right away.

So anyway, I mostly use indices, and use both raw and smart pointers sparingly. I like indices and the kinds of doors they open up when you know your objects are stored contiguously, and not scattered across the memory space.