[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

5.9.1 Correctly Using Smart Pointers

Smart pointers are a new feature in Crystal Space 0.95. The purpose of smart pointers is to make it easier to manage reference counting. So instead of manually taking care of calling IncRef and DecRef on your objects you use smart pointers (csRef) and let those take care.

How to Use Smart Pointers

This is easy. For example, often you want to keep track of a few common objects in your main class (like the pointer to the engine and so on). To do this you just declare in your main class:

 
class MyClass
{
  csRef<iEngine> engine;
  ...
}

Smart pointers don't need to be initialized. So there is no need to do `engine = 0' in the constructor of your class. There is also no need to clean them up. The reference to the engine will be cleaned up automatically when the instance of MyClass is deleted.

To fill the engine smart pointer with a pointer to the engine you can use the following code (assuming the engine plugin is loaded):

 
engine = CS_QUERY_REGISTRY (object_reg, iEngine);
engine->CreateSector (); ...

So that's it. I will show you how this would be written before smart pointers existed:

 
class MyClass
{
  iEngine* engine;
  ...
}

MyClass::MyClass ()
{
  engine = 0;
}

MyClass::~MyClass ()
{
  if (engine) engine->DecRef ();
}

  ...
  engine = CS_QUERY_REGISTRY (object_reg, iEngine);
  engine->CreateSector (); ...
  ...

The advantage doesn't seem huge but in general it is a LOT easier to use smart pointers. Especially when you have multiple exit points (error handling). The nice thing about smart pointers is that you can use them exactly like you would use a normal pointer (i.e. you can do things like engine->CreateSector ()).

Here is another very common example on how you can use smart pointers:

 
csRef<iMeshWrapper> sprite(engine->CreateMeshWrapper(...));
csRef<iSprite3DState> state (SCF_QUERY_INTERFACE (
    sprite->GetMeshObject (), iSprite3DState));
state->SetAction ("default");

What About csPtr?

csPtr is a companion class which helps in the transition from the old (pre-smart pointer) API to the new one. Basically the idea is that all functions that used to return a pointer that the caller had to DecRef will now return a csPtr. A csPtr is basically a simple pointer container. It just represents the pointer itself. csPtr will not do any IncRef or DecRef. It just stores the pointer. There are two ways to use the result of a function that returns a csPtr:

 
// Declaration of function.
struct iEngine
{
  virtual csPtr<iLight> CreateLight (...) = 0;
  ...
}

// First way to use this function (old style API):
iLight* light = engine->CreateLight (...);
// Use 'light' pointer.
...
// DecRef() when ready.
if (light) light->DecRef ();

// Second way to use this function (new style API):
csRef<iLight> light (engine->CreateLight (...));
// Use 'light' pointer.
...

So when a csPtr is assigned to a csRef the pointer will be transfered into the csRef without an additional IncRef (i.e. the csRef inherits the reference from the csPtr). On the other hand when the csPtr is simply assigned to the normal pointer then nothing special will happen so the caller is responsible for calling DecRef manually.

By using csPtr we are able to make the API smart pointer aware without sacrificing API compatibility.

To make it easier for functions to actually return a csPtr when they are internally working with a csRef there is also an automatic conversion from csRef to csPtr. This means that the following construct is ok:

 
csPtr<iBla> MyFunction ()
{
  csRef<iBla> bla = ...;
  ...
  return csPtr<iBla> (bla);
}

Basically what happens above is that the csPtr constructor which takes a csRef will call IncRef on the pointer. This is needed because when the csRef inside MyFunction() goes out of scope it will call DecRef potentially destroying the reference.

However, the following is wrong:

 
iBla* MyFunction ()
{
  csRef<iBla> bla = ...;
  ...
  return bla;
}

Because here nothing will call IncRef on the returned pointer but the csRef will still call DecRef. If you really want to return a normal pointer (which is probably not a good idea) in this case then you must first call IncRef manually before returning.

Another way to use csPtr is as follows:

 
csRef<iView> view;
view = csPtr<iView> (new csView (...));

When making a new object (new csView) there is automatically one reference already. So when assigning that to a csRef you need to encapsulate the pointer in a csPtr first so that no additional IncRef is called. The following code would have caused a memory leak because there would be two references to the view:

 
csRef<iView> view;
view = new csView (...);  // Don't do this!

WARNING Only use csPtr in the situations described above! Never use a csPtr to store something. Never pass csPtr instances as parameters to some other function.

To Clarify: What to do With a Function Return Value?

In the current Crystal Space API there are now three possible return values for functions (assuming the function returns a pointer):

Warnings About csPtr!

As already noted above, only use csPtr for returning already incremented references and for wrapping a new object before storing it in a csRef. Do not use it for anything else.

Also, when a function returns a csPtr you MUST assign the result to either a csRef or else a normal pointer (and call DecRef) later. If you don't do that then you will have a memory leak (or reference leak). i.e the following code is illegal because it ignores the returned mesh wrapper:

 
engine->CreateMeshWrapper (...);	// Don't do this!

Note that if you are in debug mode then CS will add a runtime test for this bad usage (i.e. your program will crash when you use something like the above).

Warning About IncRef and DecRef!

When using smart pointers (i.e. csRef) correctly you should be careful when using IncRef() and DecRef() manually. Avoid constructs like this:

 
csRef<iMeshWrapper> mesh = ...;
...
mesh->DecRef ();
mesh = 0;

As this will cause two references to be decremented! And that is most likely not what you want.

Warning About DestroyApplication!

Due to the way the current implementation of csInitializer::DestroyApplication() works, you MUST make sure that all your references to Crystal Space objects are released BEFORE calling DestroyApplication. So the following code is not good:

 
int main (int argc, char* argv[])
{
  ...
  csRef<iPluginManager> plugin_mgr (CS_QUERY_REGISTRY (object_reg,
  	iPluginManager));
  ...
  csInitializer::DestroyApplication (object_reg);
  return 0;
}

The reason this doesn't work is that the `plugin_mgr' reference will only be cleaned up after DestroyApplication is called. To fix this you can use several techniques. Manually setting `plugin_mgr' to 0 right before calling DestroyApplication is one way. Another way is to put the initialization into another routine so that `plugin_mgr' is created in another scope.

For the same reason it is also not good to call DestroyApplication from within the destructor of your main class. If you do that then when your main instance is deleted, first DestroyApplication is called and only then are the smart pointers released which your main instances may have.

Include Files

The include files useful for this section are:

 
#include "csutil/ref.h"


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

This document was generated using texi2html