Sunday, November 6, 2011

Smart pointers are dumb

C++ requires one to manage heap memory. Memory management can be tedious and error-prone. If different things reference this memory, and you de-allocate the memory without someone knowing, bad things can happen. If you forget to de-allocate the memory, you have a memory leak.

One solution is to use smart pointers. They let a programmer use similar syntax as normal pointers, with the benefit of memory being automatically cleaned up when nothing references that memory.

What you’ve gained by using smart pointers:

  • Anxiety reduction as you believe you are insulated from having to think about memory management
  • Potentially less typing

What you’ve lost by using smart pointers:

  • Stuff is now happening behind the scenes, but the syntax looks as though nothing is happening behind the scenes. Operators are overloaded. Evil is in the air!
  • The timing of memory de-allocations is implicit. Hidden both in time and in the code.

Normally I’m fine with memory management being done behind the scenes. Our tools are written in C#, a language with a memory manager built in, and I could care less about memory. Tools can be memory hogs to a certain (certainly huge) extent.

However, our runtime game needs to care. I want the easiest, most transparent way to manage memory.

To ease memory management in C++ I do the following:

  • Memory allocations using “new” are only allowed when a chunk (or level) of the game is loaded. Not frame to frame. Likewise, de-allocations are only allowed when a chunk (or level) of the game is unloaded.
  • I use a naming convention for class attributes. An “m” prefix means member, an “mp” prefix means a member that is a pointer, and that the class is responsible for allocating and de-allocating its memory. Finally, I use “mpe” for member pointers for whose memory the class is responsible for neither allocating nor de-allocating. The “e” means external.
  • I use simple macros to ease the typing related to allocation/de-allocation.

So, I centralize (in time) allocations and de-allocations, choose names that immediately identify a class’s responsibility, and wrap some of the clunkiness (such as checking for null before a de-allocation, setting to null after a de-allocation) in macros.

Some people use memory pools to achieve centralizing allocations and preventing a ton of frame-to-frame allocations. If I weren’t the only programmer on this project I would research this. As it stands my solution is easy to implement and transparent, and I can’t waste time researching “best” solutions to a problem that’s under control.

3 comments:

  1. I am so happy for you, Goat. Thanks to this good samaritan you can now educate yourself further on the feeding of goats.

    Hooray.

    ReplyDelete
  2. This is interesting stuff to think about. Until recently, I hadn't had any experiences where ownership of objects was something I needed to think about. Recently, however, I created a design for some test code that required instantiation (deep and wide) of more objects that I normally deal with. I had some memory issues when I first ran with it, and it took me several hours to track it all down and make corrections, but I eventually got it all sorted out.

    Anyhow, it's good to know of the existence of smart (dumb) pointers, which I was largely ignorant about until we talked about them yesterday and read your post. I also like your naming convention, which I'll keep in mind if I ever need to employ it.

    ReplyDelete