UnigineEditor
Interface Overview
Assets Workflow
Settings and Preferences
Working With Projects
Adjusting Node Parameters
Setting Up Materials
Setting Up Properties
Landscape Tool
Using Editor Tools for Specific Tasks
Extending Editor Functionality
FAQ
Programming
Fundamentals
Setting Up Development Environment
Usage Examples
UnigineScript
C++
C#
UUSL (Unified UNIGINE Shader Language)
File Formats
Rebuilding the Engine and Tools
GUI
Double Precision Coordinates
API
Containers
Common Functionality
Controls-Related Classes
Engine-Related Classes
Filesystem Functionality
GUI-Related Classes
Math Functionality
Node-Related Classes
Objects-Related Classes
Networking Functionality
Pathfinding-Related Classes
Physics-Related Classes
Plugins-Related Classes
Rendering-Related Classes

5. Working with Smart Pointers

<< RETURN TO THE PREVIOUS SECTION

In UNIGINE, instances of C++ API classes (such as: node, mesh, body, image, etc.) only store pointers to instances of internal C++ classes, they cannot be created and deleted via the standard new/delete operators. So they should be declared as smart pointers (Unigine::Ptr) that allow you to automatically manage their lifetime. UNIGINE has its own optimized memory allocator for faster and more efficient memory management.

To create an instance of an internal class we should declare a smart pointer for the class we are going to instantiate and call the create() method - class constructor - providing construction parameters if necessary.

Source code (C++)
// instantiating an object of an internal class
<Class>Ptr instance = <Class>::create(<construction_parameters>);

All objects are divided into two groups regarding the way their lifetime is managed:

Ownership Objects#

Ownership objects (Image, Texture, Mesh, Tileset, etc.) — these objects are managed in accordance with reference counter, i.e. how many smart pointers are pointing to the managed object; when the last smart pointer is destroyed, the counter goes to 0. In this case it is assumed that the object is no longer needed (the Engine doesn’t know anything about it, and the user has got no pointer to be able to use it) and, therefore, it is deleted. (e.g. such objects declared within a scope will be automatically deleted when leaving the scope).

Source code (C++)
// creating a new image
ImagePtr img = Image::create();

// now two pointers point to our image (reference counter increment)
ImagePtr img2 = img;

// removing the image (as both pointers no longer point to it and reference counter is zero)
img2 = img = nullptr;

// another way to clear pointers
img.clear();
img2.clear();

Notice
You should avoid cyclic references!
If there is a ring, or cycle, of objects that have smart pointers to each other, they keep each other "alive" - they won't get deleted even if no other objects in the universe are pointing to them from "outside" of the ring. This cycle problem is illustrated in the diagram below that shows a container of smart pointers pointing to three objects each of which also point to another object with a smart pointer and form a ring. If we empty the container of smart pointers, the three objects won't get deleted, because each of them still has a smart pointer pointing to them.

Cyclic references

Non-Ownership Objects#

Non-ownership objects (nodes, widgets, materials, properties, etc.) — these objects interact with the Engine and become managed by it since the moment of their creation (they are engaged in the main loop, can be retrieved by names, etc.). The lifetime of these objects is not determined by the reference counter, they provide the mechanism of weak references, so you can check whether an object was deleted or not. To delete such objects you should use deleteLater() or a corresponding manager’s method (e.g.: Materials::removeMaterial()).

Source code (C++)
NodePtr node;
void somefunc1(){
// creating a new dummy node
node = NodeDummy::create();
}

void somefunc2(){
// checking whether the node exists
if (node)
Log::message("The node is alive\n");

// deleting the node
node.deleteLater();
}

Instead of managing references for nodes manually now you can simply choose lifetime management policy for it:

  • World-managed - in this case a node shall be deleted when the world is closed. This policy is used by default for each new node.
  • Engine-managed - in this case the node shall be deleted automatically on Engine shutdown (can be used for nodes that should be kept when changing worlds).
Source code (C++)
NodePtr node = NodeDummy::create();
NodePtr node2 = NodeDummy::create();

// the node shall be deleted on world shutdown
node->setLifetime(Node::LIFETIME_WORLD);
// the node2 shall remain alive even when another world is loaded
node2->setLifetime(Node::LIFETIME_ENGINE);
Notice
Lifetime of each node in the hierarchy is defined by its root (either parent or possessor). Setting lifetime management type for a child node different from the one set for the root has no effect.

Additional information:

PROCEED TO THE NEXT SECTION >>

Last update: 2019-12-25