Memory Management
ClanLib provides several different kinds of memory management interfaces. They can be roughly divided into two different categories:
- Tracking allocation or freeing of objects
- Memory allocators
Memory Management
ClanLib currently has two memory management classes; CL_DataBuffer and CL_SharedPtr<Type>.
CL_DataBuffer is a class that helps allocating a block of memory and freeing the memory when all references to the buffer is gone. A simple usage example where we retrieve job information from the Windows spooler:
DWORD size_required = 0; GetJob(printer, job_id, 2, 0, 0, &size_required); CL_DataBuffer buffer(size_required); GetJob(printer, job_id, 2, buffer.get_data(), buffer.get_size(), &size_required); JOB_INFO_2 *job_info = (JOB_INFO_2 *) buffer.get_data(); CL_Console::write_line("Document was printed by %1", job_info->pUserName);
The main point of using the data buffer class is that it is guaranteed that the above code will free the memory for JOB_INFO_2 no matter what exit path used, including any exceptions thrown.
The CL_SharedPtr template is an improved smart pointer. The idea is roughly the same as above with the data buffer class, except it uses C++ templates to allow you to treat the CL_SharedPtr as if it was a normal pointer. A simple example:
class MyClass { public: int a; void test() { } }; CL_SharedPtr<MyClass> ptr(new MyClass); ptr->a = 314; ptr->test();
Once again same principle; when all references to the shared pointer is gone the instance of MyClass will be deleted.
The shared pointer template in ClanLib includes a number of tricks to avoid heap allocations and it allows you to fine-tune what is to be done when the references reach zero. In the above example, the only heap allocation was the 'new MyClass' command. Here's a more advanced example where we use a callback when it is time to free our class:
class MyOtherClass { public: void free_myclass(MyClass *ptr) { delete ptr; } }; MyOtherClass other_class; CL_SharedPtr<MyClass> ptr( new MyClass, &other_class, MyOtherClass::free_myclass); ptr->a = 314;
We can also use the built-in memory allocator interface in ClanLib to allocate and free the memory. In this example we allocate the object on the stack using a 16 kilobyte fixed memory pool:
CL_FixedMemoryPool<16*1024> memory_pool; CL_SharedPtr<MyClass> ptr(cl_new(&memory_pool) MyClass, &memory_pool)); ptr->a = 314;
Controlling your memory like this allows your application to vastly improve allocation performance, especially in multithreaded environments.
The reference counting in CL_SharedPtr does not support circular references; i.e. when object A has a pointer to object B and object B has a pointer to A. To solve this problem, the CL_WeakPtr<Type> template class is available:
class A { public: CL_SharedPtr<B> b; }; class B { public: CL_WeakPtr<A> a; }; CL_SharedPtr<A> a(new A); a->b = CL_SharedPtr<B>(new B); a->b->a = a;
When all references to A are gone, B is freed as well. But losing all references to B will not free A.
One last feature of CL_SharedPtr allows you to place any CL_SharedPtr object with the non-template class CL_UnknownPtr. You can then later 'cast' the object back into a CL_SharedPtr. If the object does not match the shared pointer type, the shared pointer will end up pointing at null.
CL_SharedPtr<A> a(new A); CL_SharedPtr<B> b; CL_SharedPtr<A> c; CL_UnknownPtr unknown = a; b = unknown; // b.is_null() returns true c = unknown; // c now points at a
Allocators
ClanLib implements a replacement for the conventional operator new, delete and delete[]:
- cl_new(pool) ClassName
- cl_delete(pool) ptr
- cl_delete_array(pool) ptr
These replacements will use the CL_MemoryPool object passed to allocate and free the specified memory, instead of using the generic heap allocator provided with the compiler. This allows an application to much better fine-tune its memory allocations; an application could use different memory pools for temporary and persistent data, it could use different pools for each thread and so on.