Choose appropriate pointer, choose appropriate memory management model

Recently I decided to start devoting my part time on writing some code in C++. It's my first time to use C++ to write some real complicated code, a small game framework, include scene graph, entity, component, event system, etc.
For my pasted experience on Delphi, J2ME and Flash development, I didn't have any trouble to think about memory management because those platforms/languages don't give us many choices.
But with C++, we have too many choices as my question posted on gamedev.net.
After some struggling with raw pointers and homemade shared pointers, now I removed all silly homemade shared pointers and nailed down the policy to use pointers and manage memory.
Now I would like to share them with you if you are in same struggling as I was.

Policies:

  1. Avoid shared pointer when possible. Aka, clear object ownership.
  2. Use scoped pointer and RAII when possible. Aka, avoid raw pointers and explicit "delete".
  3. Only use shared pointer when resources must be shared. Aka, avoid shared pointer when possible.
  4. Use consistent function naming convention to indicate ownership.
  5. Manually reference count is OK for internal restricted use to instead of shared pointer, as long as the reference count is not exposed to public.

 1, Avoid shared pointer when possible.

There is nothing wrong with shared pointer (shared_ptr in boost library) itself. The problem is that shared pointer utilizes implicit object ownership.
It's always good to prefer clear, explicit, and strong object ownership to vague, implicit, and weak object ownership, no matter what platform and language, even include Java that doesn't have explicit ownership semantic.
 
Let's take an example from game development.
Say we have three modules: Game object (a kind of entity), object pool or scene graph, and updating system.
Pool will own game objects. Updating system will use game objects, but not own them, and may pass the entity pointer to any arbitrary sub systems. Pool and updating system exist for ever, and game objects may be created/destroyed at run time.
If we stick to explicit object ownership, and only pass raw pointers, or any non-sharable pointers to update system, then developer who is adding any sub system to the updating system will know that his sub system will not own the pointer and will not hold it after the entity is destroyed.
But if we pass shared pointer to update system, the ownership is vague, and a developer may create a new sub system and hold the pointer for life even after the entity is removed from object pool.
That's a kind of seriously memory leak, and more seriously, no any tool can detect that kind of leak since it's not leak from technical view. It's a logical mistake.
 
There are some other flaws of shared pointer, though they are minor comparing the implicit ownership one.
Need weak pointer to avoid circular reference. It's a pain to use different point for same object.
Memory overhead. Reference count data needs to be allocated dynamically.
Can't use for some complicated data structure. I used quad linked tree for scene graph, it has node that points to self. It will be hell to use shared pointer for it.

2, Use scoped pointer and RAII when possible.

The advantage to use scoped pointer (scoped_ptr in boost library, unique_ptr in C++ TR1) than raw pointer, is not only scoped pointer can release resource when it's not used. The biggest advantage is that a scoped pointer implies the explicit object ownership. Whenever a developer sees a scoped pointer, he knows that he can't own the pointer even he can use it. That's better than any comment.

3, Only use shared pointer when resources must be shared.

There should be very very few resources that must be shared. If there is any, I would rather think if I can avoid share it first. If the resources have to be shared, use shard pointer.

4, Use consistent function naming convention to indicate ownership.

Prefix "create", "alloc" and "new" indicate the function doesn't own the object. All other prefix indicates the function owns the object and the function user should not free the result. Apple has very good guide in their Cocoa memory management guide.
Examples:
// you have responsibility to release the Entity pointer after used it.
Entity * createEntity();
 
// you must not release the Entity pointer, you don't own it.
Entity * getEntity();
 
With this kind of naming convention and scoped pointer, we almost have no need to comment for object ownership.

5, Manually reference count is OK for internal restricted use to instead of shared pointer, as long as the reference count is not exposed to public.

Under certain circumstance, this works quite well and has no overhead from shared pointer.
For example, I had used manually reference count (calling addReference/releaseReference manually) in a container to manage the internal node that's used to wrap the container items. No any other code, except the container itself, knows the reference count. So as long as the container works correct, the reference count works correct.

Summary:

The rules/policy for memory/pointer management are what I'm using and will use in my code. That may be quite different with yours -- there are a lot of people insist to only use shared pointer when possible. That's fine as long as it works for you.
Any way, in my opinion, clear object ownership and avoid vague ownership is always good.
 
 

Write a comment

  • Required fields are marked with *
  • Security Code is case sensitive, 'A' is different with 'a'.

If you have trouble reading the code, click on the code itself to generate a new random code.
Security Code: