Article: Reference counting

From DaevsGUI

Jump to: navigation, search

WARNING: This page is outdated

Contents

General

This GUI system uses a reference counting system. This means that we have to be careful to not delete any data that is still being in use (or to remove data that is not being used, same thing). This brings some complexity to the code we have to cope with, since it allows us to make a program use as less memory as possible. This requires some thinking when using the library and doesn't make it easier to handle. However, I think the advantages (less memory, less copying around) outweigh the disadvantages (more complex memory handling)...consider a texture atlas to be used by multiple components, you don't want to safe a copy per element that uses it.

Basically, whenever passing (a pointer to!) an object that keeps track of reference counting (most objects in the library) you can choose 3 modes of how it should be used (called ReferenceMode's):

  • New: take the pointer and use it
  • Copy: reallocate a new pointer and use the copy constructor to copy the data
  • Share: take the pointer, increase reference count, and use it

Whenever a container, for example, does not use a component no more, it will lose reference on that object. If the container was the only object using it, it would deallocate it. This means the following:

  • New: library will take care of removing, don't delete or 'lose' on the pointer the user has
  • Copy: library has it's own copy, so user would delete his copy as he normally would (or 'lose' up on it, but that doesn't make much sense if only passed as copy!)
  • Share: user would need to call 'lose' on the pointer, since it might still be used by the library


The ReferenceMode is set to New, when no ReferenceMode is explicitly given by the user!

Gaining or losing

To increase or decrease the reference counter, we have 2 global functions: gain() and lose(). The first increases the counter, the second either decreases or, when counter is 0 (zero means: zero _other_ objects use it), deletes it and sets the pointer to NULL. Both function work on pointers of any object (the object must have a function called 'References &references();').

So, whenever you'd _retrieve_ an object from the library and want to use it, gain() on it. You can now safely delete the objects that hold a pointer to this object you retrieved, it won't be deleted and you can safely use it. When you're done, call lose() on it, and the library will decide to keep it allocated or to deallocate it (depending on the number of objects that still use it).

To be 100% safe, one would use lose() over delete. However, this is of course not necessary if you pass things in 'copy' reference mode, and neither lose() nor delete should be used if passed in 'new' reference mode.

Internal usage

This is how gain() and lose() are defined:

template <class T>
T *gain(T *instance)
{
    instance->references().gain();
    return instance;
}
 
template <class T>
void lose(T *instance)
{
    if (!instance->references().lose())
    {
        delete instance;
    }
}

Example code

New

enum
{
    COMPONENT
};
 
DaevsGUI::Container *container = new DaevsGUI::Container;
DaevsGUI::Component *component = new DaevsGUI::Component;
 
// add component, and let the library handle the memory management
container->add(COMPONENT, component, DaevsGUI::New); // equals container->add(COMPONENT, component);
// component points to the same data as the pointer in container points to
// however, the library *could* delete the component when it isn't used no more
// so using component after adding it as a 'New' isn't fool-proof
 
// we know it still exists in this simple example, but it can be very unsafe
component->rect(10, 10);
 
// a little safer, will throw an exception when it doesn't exist no more
container->get(COMPONENT)->rect(10, 10);
 
// remove component from the container
container->remove(COMPONENT); // equals container->remove(component); container removes all element at the end of it's lifetime though
// component is now deleted!
 
delete container;

Copy

enum
{
    COMPONENT
};
 
DaevsGUI::Container *container = new DaevsGUI::Container;
DaevsGUI::Component *component = new DaevsGUI::Component;
 
// add component, and let the library take a copy of the object
container->add(COMPONENT, component, DaevsGUI::Copy);
 
// this will not affect the component we just added to the container
component->rect(10, 10);
 
// this affects the component we just added to the container
container->get(COMPONENT)->rect(10, 10);
 
// remove component from the container
container->remove(COMPONENT);
// component stored in the container is now deleted!
 
delete component;
delete container;

Share

enum
{
    COMPONENT
};
 
DaevsGUI::Container *container = new DaevsGUI::Container;
DaevsGUI::Component *component = new DaevsGUI::Component;
 
// add component and increase reference count
container->add(COMPONENT, component, DaevsGUI::Share);
 
// we can safely use the component pointer, this affects both this component and the one the container holds
component->rect(10, 10);
 
// same as above
container->get(COMPONENT)->rect(10, 10);
 
// remove component from the container
container->remove(COMPONENT);
// component decreases reference count, but is not deleted
 
lose(component); // in this simple example we could also simply use delete, however, in anything bigger it's best to be safe
delete container;
Views
Personal tools
Other