Referenced Type Objects¶
Overview¶
In Choreonoid, many objects created through dynamic memory allocation are defined as classes that inherit from the Referenced class. Such classes are held with ref_ptr type pointers, which are Choreonoid’s own smart pointers. This pattern is used throughout the Choreonoid SDK, and you will likely use it frequently when creating your own plugins. Therefore, this section explains the basics of creating and holding such Referenced type objects.
Dynamic Object Creation and Smart Pointers¶
Objects with relatively long lifespans that are shared from multiple parts of a program are typically created with the new operator, placed in dynamically allocated memory, and accessed through pointers. In this case, when the object is no longer needed, it must be deleted with the delete operator to clean up the object and free the memory. However, coding this process without omissions is a burden for programmers, and it often leads to problems such as memory leaks when objects are not deleted.
Therefore, it has become common practice to hold dynamically created objects referenced by pointers using so-called smart pointers. When held by smart pointers, the object is automatically deleted when the pointer variable goes out of scope and the object is no longer referenced from anywhere. C++ provides smart pointers such as std::unique_ptr and std::shared_ptr in its standard library, and by using them, you can more reliably ensure the destruction of dynamically created objects.
Choreonoid’s implementation also uses standard library smart pointers, but it also uses its own object types and specialized smart pointers. Therefore, in Choreonoid plugin development, it is necessary to understand the differences between the two and use them appropriately.
Referenced Type Objects and ref_ptr Type Smart Pointers¶
In Choreonoid’s implementation, the “own object types and specialized smart pointers” correspond to Referenced types and ref_ptr types, respectively. These implement a mechanism for sharing dynamic objects using reference counters built into the objects.
When using this to hold dynamically created objects, define the object’s class by inheriting from the Referenced type. For example, to apply this to a class called Object, you would do the following:
#include <cnoid/Referenced>
class Object : public cnoid::Referenced
{
public:
void function();
...
};
This makes the Object class a Referenced type. This class is typically created with new. Then use ref_ptr type smart pointers to hold the created pointer. This can be written as follows:
cnoid::ref_ptr<Object> object = new Object;
The object can be handled like a regular pointer (raw pointer). For example, member functions of the referenced object can be executed as follows:
object->function();
Since writing ref_ptr every time makes the description somewhat complex, it is common to define it in advance as a pointer type specific to this class using typedef. In this case:
typedef ref_ptr<Object> ObjectPtr;
and then typedef it, so you can write:
ObjectPtr object = new Object;
When typedef’ing a type name like this, use the naming convention “ClassName + Ptr”. By following this rule, you can recognize that the typedef’d type name is a smart pointer type.
Since it’s a smart pointer, the referenced object is also destroyed when this pointer is destroyed. Therefore, you don’t need to explicitly execute delete on the object, and you shouldn’t. For example:
{
ObjectPtr object = new Object;
...
}
In this case, object is also deleted when this scope ends.
Of course, actual use cases rarely involve such simple scopes. In practice, this pointer is defined as a member variable of other objects and referenced from there, or passed to multiple objects. References by ref_ptr allow multiple pointers to redundantly reference the same object. In that case, the object is destroyed when it is no longer referenced by any pointer.
As you can see from the above, this is basically the same as std::shared_ptr, as it is a smart pointer that allows multiple pointers to share and reference an object. The method to achieve this is also basically the same, as both use reference counters.
weak_ref_ptr¶
For shared_ptr, weak_ptr is available to hold weak references to target objects. Similarly, weak_ref_ptr is available for holding weak references for ref_ptr. The usage is almost the same as weak_ptr. It can be created from ref_ptr as follows:
ref_ptr<Object> object = new Object;
weak_ref_ptr<Object> wobject = object;
Like ref_ptr, it can also be created from raw pointers:
weak_ref_ptr<Object> wobject = new Object;
Like weak_ptr, you can create ref_ptr with the lock function:
if(ref_ptr<Object> obj = wobject.lock()){
...
}
Other functions such as reset and expired can also be used in the same way as weak_ptr.
Making Custom Classes Referenced¶
When defining custom classes in plugin implementation, it is also possible to inherit from Referenced type and reference them with ref_ptr. When inheriting from existing classes that are Referenced types, newly defined classes will inevitably also be Referenced types, but otherwise it is not necessarily required to make them Referenced types. Classes that are intended to be dynamically created using new and that you want to reference with smart pointers are candidates for Referenced types. Even if there is a possibility of using them in this way, if there is also a possibility of using them directly as automatic variables or class member variables, they should not be made Referenced types. Such classes should be referenced with shared_ptr only when they are dynamically created and used.