Wolf Wolf - 1 month ago 8
C++ Question

How to prevent converting a null pointer into a reference

In my application, I have a Singleton that represents "the system". This object is created first and destroyed last and all other objects are supposed to be managed by it, which means, each object will be created directly or indirectly by this system object (in its

run
method) and all objects will (hopefully) be removed before the system gets unavailable.

The problem is that an optional access to the system object (via pointer) introduces lots of not-null checking code. I know that dereferencing a null pointer results in undefined behaviour. Is using the good old C-
assert
the best I can do?

// this is mostly pseudo-code, but should compile if
// there is an appropriate MySys header
#include "MySys.h"

class MySysImplementation: public MySys
{
public:
MySysImplementation();
void run();
// ...
};

/// pointing to system object during runtime
namespace {
MySys* g_sys = NULL;
}

MySys& MySys::Instance()
{
assert(g_sys);
return *g_sys;
}

main ()
{
{
MySysImplementation sys;
g_sys = &sys;
sys.run();
} // <---------- HERE all application-specific objects should be gone
g_sys = NULL; // in real code this RAII-ensured
}

Answer

While asserts can be removed in a release mode you may be better of by throwing an exception instead.

Your program may crash by just using the null pointer without exception but this is undefined behavior and can have all possible side effects.

An exception also has the advantages that the objects you created get cleaned up (stack unwind) and you also have the chance to catch the exception and deal with it if you have a part of your code where you are able to properly deal with this.

You can throw whatever you like, but it is recommended to throw anything derived from std::exception. std::runtime_error seems good here. Or derive something yourself.

One thing to note though:

Because you are working on a global object (singletons are also global) you should avoid using the singleton in a destructor. When an exception gets thrown and you are "stack-unwinding" and one of those objects destructors would throw a second exception the program will immediately call std::terminate. If you terminate anyway this might not be a problem, but if you want to catch the exception somewhere or you rely on stack-unwinding this may lead to issues.