Memory management conventions depend on the type of the object being managed. However, there are only a few possible memory management schemes that are used consistently throughout the Inti libraries.
This is the memory management you would expect for a built-in C++ type. The rules include:
The object may be placed on the stack, or allocated on the heap with the new operator.
It's safe to call delete on the object, if it was allocated with new.
If the object is returned by pointer rather than by value or reference, the recipient of the object must call delete on the returned value. For example, if a method returns a Gdk::Rectangle*, the caller of the method must delete the Gdk::Rectangle. However, more typically these objects are returned as a const reference or as a value.
Strict reference counting is very painful to use, so it's generally avoided in Inti. However it's simpler to explain than the reference counting Inti uses most often (see the section called Reference counting with a "floating" reference).
The strict_ptr<T> smart pointer class is intended for use with these objects. strict_ptr<T> increments the reference count on the object it points to. Note that the following code is dangerous:
strict_ptr<Button> button = new Button; |
Strictly refcounted objects follow certain rules:
The object implements the two methods ref() and unref(). These increase and decrease the reference count, respectively.
Object instances must be created with the new operator. All instances are created with a reference count of 1.
When the object's reference count reaches 0, the object is deleted (it calls delete on itself, invoking the destructor and freeing memory).
Calling delete is forbidden; the destructor is generally protected or private, though if you subclass an object the destructor will become public by default (declare a protected destructor in your subclass to avoid this).
The object is passed and returned by pointer. Any code section that retains a pointer to the object should increment its reference count during the time the pointer is retained. Reference counts are never implicitly passed from one owner to another; if you own a reference, you must always call unref () eventually.
Reference counted objects always have private copy constructors and assignment operators. These objects may not be passed by value.
This is the memory management scheme used by most nontrivial objects in Inti, including all widgets. It's identical to strict reference counting (see the section called Strict Reference Counting), with a twist: the object can have a "floating" reference count, which is not owned. Any code section can "sink" the floating reference count; once sunk, the object effectively has strict reference counting. There is no way to "unsink" an object.
The purpose of the floating reference is to keep the object alive until it finds its first owner. An owner can "adopt" an object by adding a reference count, and removing the floating reference. Consider the following code:
Window * win = new Window;
win->add (new Button ("My Button"));
|
Window * win = new Window;
Button * button = new Button ("My Button");
win->add (button);
button->unref (); |
The ptr<T> smart pointer class is intended for use with floating-reference objects. ptr<T> will call ref() and then sink() on any object assigned to the pointer. strict_ptr<T> may also be used with these objects; however it will never call sink (), only ref(). strict_ptr<T> is useful if you don't want to change the floating status of an object, but it risks memory leaks, because strict reference counting is very hard to use without leaking memory.
The following code works nicely with floating references and ptr<T>, in constrast to strict_ptr<T> and strict references:
ptr<Button> button = new Button; |
Here are some of the rules floating reference counted objects follow:
The object implements the two methods ref() and unref(). These increase and decrease the reference count, respectively.
The object implements the method sink(). If the object has a floating reference, then sink() removes it. Otherwise, sink() does nothing.
Object instances must be created with the new operator. All instances are created with a reference count of 1; this initial reference is floating. Calling sink() on the newly-created object will cause the reference count to reach 0, and the object will be destroyed. (A call to unref() is not permitted on newborn objects, because no one owns the floating reference; if you want to own a reference, you must call ref() first, to obtain a reference.)
When the object's reference count reaches 0, the object is deleted (it calls delete on itself, invoking the destructor and freeing memory).
Calling delete is forbidden; the destructor is generally protected or private, though if you subclass an object the destructor will become public by default (declare a protected destructor in your subclass to avoid this).
The object is passed and returned by pointer. Any code section that retains a pointer to the object should increment its reference count during the time the pointer is retained. Any code section has the right to call sink (), removing the floating reference count. For example, if you add a child widget to a container widget, the container widget will add a reference to the child with ref(), and then call sink() to be sure the child is no longer floating.
Reference counted objects always have private copy constructors and assignment operators. These objects may not be passed by value.