YetiSim Blog

Blogs about simulation and developing YetiSim.

Pointer and Reference Semantics

I have been refactoring YetiSim to move away from boost::shared_ptr to the new MemoryManager class, and replacing the shared_ptrs with references and pointers.  Where should references be returned or passed as arguments, versus pointers? What meaning should be inferred by the programmer based on this? For example, what does the prototype X& foo(Y& y) mean vs. X* foo(Y* y).  There must be a global standard for how to interpret pointers and references in prototypes in YetiSim, otherwise chaos will ensue.

After some discussions on #tbb, I decided to create and adhere to specific conventions within YetiSim.  These are the conventions for arguments:

  • References as constructor arguments:  A reference that is used within a constructor argument may be stored by the constructed class.  The class does not assume ownership of the referenced data, but may store an internal reference to it for future use.  Be careful of this situation for class destruction.
  • References as function arguments:  If a reference has been passed as an argument to a function, then the function is free to use that argument for the duration of the call.  Unless explicit in the documentation, the reference will not be saved for future use.  There should be very few, if any, cases of functions saving references for later use.
  • Pointers as a constructor argument: When a pointer is used in a constructor, ownership of the data is transferred to the constructed class.  This class is now responsible to calling appropriate MemoryManager functions to delete the data.  The pointer may naturally be saved for future use, however when passing the data to other functions or classes it should now be done by reference to convey ownership is not being passed.
  • Pointers as a function argument:  The function which has been called via a pointer assumes responsibility for ownership.  The function may save the pointer for future use, but the function (or class if a member function) is now responsible for deleting the data by calling appropriate MemoryManager member functions.

Similarly there are conventions for the return part of the function prototype.

  • References as a return type: If a reference is returned, then the object may be freely used, but ownership is not acquired.  The reference may be stored for future use, but this has to be done with care in case the reference disappears.  There must be a guarantee that the object cannot hold the reference if the referenced object disappears.  A good example of this, is  the ThreadOfExecution class.  It holds StateMachineExecution references, but part of the garbage collection of a StateMachineExecution is to delete the ThreadOfExecution objects.  Thus, a ThreadOfExecution could never reference a defunct StateMachineExecution object.
  • Pointers as a return type: This represents a transfer of ownership from the called function.  It means that the called function will no longer be responsible for the destruction of the object.  If you called the function that returned a pointer, you now own that memory.  You are now responsible for its destruction, or passing ownership to someone else.

Another complexity is the use of references to access an object that might have been destroyed after throwing an exception.  This complexity must be part of the exception handling strategy, and must be considered within the design itself.  For now, I ignore this problem because it will have to be dealt with in the exception handling strategy.

So in summary, a pointer means transfer of ownership.  A reference means that you can just use the object freely, storing references to it or otherwise so long as you know that it won’t be destroyed when you don’t intend for it to be.  These are the global semantics for YetiSim references and pointers.  If there are changes, I will edit this blog or post a comment.


Posted by AJ Guillon  (December 13, 2007)

Leave a Reply