A new template class has been added to
BazisLib 2.01: ConstManagedPointer. References to it should be used as function/template parameters to optimize performance. See its description for details.
An object that supports reference counting is called a
managed object. Every class that represents a managed object is a direct or inderect descendant of the
ManagedObjectBase class. A managed object cannot be allocated in stack or statically. The only correct way to allocate such objects is to use the new() operator.
The debug build of the framework checks the newly created objects whether they were allocated from heap, however, the release build does not. All managed objects are organized in a tree-like structure. Practically, an object that wants to store the pointer to another object, should become a
client for that object. An object, that may have clients is called a
service and should be a direct or indirect descendant of the
ServiceObject class. A
root service that is not a client of anyone, should be a child (directly) of the
ServiceObject class. The object manager ensures that a service is not deleted until all its clients are freed. A client may be a service at the same time. In the simpliest case, such object can inherit the
IntermediateServiceObject class and use the
IntermediateServiceObject::GetParentService() method to get its service. However, if an object (no matter, whether it is a service or not) wants to be a client of a multiple services, it should use managed field pointers. Mind that you should avoid
circular references when designing your classes. This means, that a service cannot become a client for any of its clients. The debug build of the framework detects such problems.
If you want to store a pointer to a service inside a client, you should never do it directly. Instead, use the following scheme:
To illustrate the rules by an example, let's assume that we have two service classes: a
Disk and a
File. A
File is a client for a
Disk. Also, a
Disk object is deleted after all file objects are freed. Let's see how this example is implemented:
class Disk;
class File
{
private:
DECLARE_REFERENCE(Disk, m_pDisk);
int m_SomeVariable;
public:
File(Disk *pDisk) :
INIT_REFERENCE(m_pDisk, pDisk),
m_SomeVariable(SomeValue)
{
}
File(const File &file) :
COPY_REFERENCE(m_pDisk, file)
m_SomeVariable(SomeValue)
{
}
};
Note that we declared a copy constructor. It is required only if you want to enable copying of your objects. If not, it is not required and an attempt to perform such copying will result in a compilation error. In case you really need it, simply use the COPY_REFERENCE() macro to ensure that all pointers are updated correctly.
If you want to declare a managed pointer that will not be a field of a managed object, you should use the
ObjectManager::ManagedPointer class. It ensures that the reference counter is decreased automatically after the pointer is deleted. It also supports copying.
An unmanaged pointer is a native C++ pointer (like SomeObject *pObj). Note that if some method returns you an unmanaged pointer, you are responsible for releasing it by calling
ManagedObjectBase::Release() method. However, when you initialize any type of managed pointer using an unmanaged one, it takes the ownership of the object (it does not retain the object another time, however, it releases it on deletion). Note that the only way to get an unmanaged pointer to an object is to call the
ManagedPointer::RetainAndGet() method that returns a previously retained unmanaged pointer. The described scheme implies a set of simple rules to follow:
-
When possible, avoid using unmanaged pointers. If you got an unmanaged pointer, use it to iniaialize a managed one immediately and forget the unmanaged one.
-
Use references to ConstManagedPointer class when declaring method parameters/return types
-
DO NOT use an unmanaged pointer after you passed it to some method, or initialized a managed one
A sample illustrating object manager usage is located under SAMPLES\COMMON\OBJMGR, while an autotest program is in TESTS\OBJMGR.
There are some cases that can require a service to tell its clients some information. For example, a disk object that detected device removal may want to notify the file objects about this event. Moreover, this is the only way for a service to interact with its clients. To send a message to all the clients of a service, you should call the
ServiceObject::CallClients() method. It notifies every client of the service by calling
ManagedObjectBase::ExecuteCommand() method. If one of the clients returns an error code, the method does not call other clients and return that code. On successful termination, it returns a value of CommonErrorType::Success.
An auto interface is an interface for a managed object. Actually, it is an interface that is virtually inherited from the ServiceObject class. Virtual inheritance ensures that an object implementing multiple managed interfaces contains a single instance of the ServiceObject class. See
AUTO_INTERFACE and
AUTO_OBJECT documentation for usage examples. Note that if you want to declare two equivalent interfaces, one managed and one unmanaged, use the MAKE_AUTO_INTERFACE macro. To declare a pair of classes (one for managed and one for unmanaged interface), use the following scheme:
template <class _Parent> class _Cxxx : public _Parent
{
...
};
typedef _Cxxx<Ixxx> UnmanagedXxx;
typedef _Cxxx<AIxxx> ManagedXxx;
-
Avoid circular references
-
Allocate managed objects only in heap
-
If you want to use a service from a client, use DECLARE_REFERENCE() macro
-
If you want to declare a managed pointer inside the stack, or as a parameter, or as a return type, use the ObjectManager::ManagedPointer template class.
-
If you want to notify all clients of a service, use the ServiceObject::CallClients() method
-
An unmanaged pointer is always a retained one, whenever you see one. This fact is used in managed pointer constructors. So, DO NOT use an unmanaged pointer AFTER you create a managed one based on it.