ADTF
|
The uCOM offers some functionality to share ownership of objects through pointers. For this purpose, the STL offers several so called smart pointers, e.g. std::shared_ptr
, std::weak_ptr
or std::unique_ptr
. However, these smart pointers do have the major drawback that they cannot be used to share resources between binary boundaries. E.g. compiling an STL smart pointer in release mode leads to an entire different object layout than in debug mode. To realize a shared ownership even between binary boundaries, the uCOM offers an own reference counting mechanism which is implemented with object_ptr
and iobject_ptr
.
To use reference counting on objects through pointers with object_ptr
and iobject_ptr
, some things must be kept in mind:
IObject
make_object_ptr()
must be useducom_cast()
Given the following inheritance hierarchy (taken from The ucom_cast<> in depth explanation):
To create an object_ptr
managing the ownership of an object cCar
through a pointer of IVehicle
one would have to use make_object_ptr()
.
Of course, const correctness is provided:
make_object_ptr()
is intended to work exactly like std::make_shared()
. The parameters are directly forwarded to the constructor of cCar
during construction. No 'traditional' memory allocation is needed by the user, so no new
or malloc
must be called. In fact, make_object_ptr()
is the only way to instantiate a new reference counted object. make_object_ptr()
returns the allocated object with a reference count of 1.
object_ptr
offers several constructors and assignment operators. This makes it possible to 'convert' one object_ptr
managing type A to another one managing type B - as long as type A is castable to type B with ucom_cast()
. In this example the managed resource cCar
gets casted to IVehicle
in the aliasing constructor of object_ptr
. This makes the instantiation of object_ptr
instances managing objects through interface pointers very convenient.
With the reference counting wrapped into object_ptr
, the developer doesn't need to take care of reference counting at all. Copying, moving, deleting and creating object_ptr
instances entirely takes care of the lifetime of the managed object under the hood. Just like std::shared_ptr
objects, the managed object is destroyed and its memory deallocated when either of the following happens:
object_ptr
owning the object is destroyed, orobject_ptr
owning the object is assigned another pointer via object_ptr::operator=
or object_ptr::Reset()
.The managed object is destructed and deallocated using the supplied IObject::Destroy()
method. In fact, the object_ptr
is the only class having access to this method, restricting object (de-)allocation of type IObject
to just make_object_ptr
and object_ptr
.
To ensure binary compatible reference counting, pure abstract base class iobject_ptr
is introduced. If a reference counting is required in interface methods, this class is the parameter type to use. object_ptr
being derived from iobject_ptr
ensures implicit upcasting and convenient usage on calling functions. object_ptr
also implements a conversion operator to iobject_ptr<IObject>
or iobject_ptr<const IObject>
, depending on the constness of the managed object type (object_ptr_base
).
Thus, code like this is totally valid:
Using this mechanism in functions or methods ensures an entirely binary compatible reference counting. The following code shows an example how this might be used:
Main purposes of object_ptr
and iobject_ptr
are to
Despite the fact, that it might be used as a more generalized reference counting mechanism, these requirements limit the usage of object_ptr
and iobject_ptr
to objects of types derived from IObject
. To fulfill the first requirement, object_ptr
calls the IObject::Destroy()
method to ensure the deallocator is called in the same DLL as the object was allocated. To fulfill the second requirement, object_ptr
uses the ucom_cast()
which also only works on types derived from IObject
.
Thus, code like this won't compile:
IStatusSymbol
can not be used as managed resource as it is not derived from IObject
.