使用智能指针
Some Basics一些基础知识#
In UNIGINE instances of C++ API classes (such as: Node, Mesh, Body, Image and so on...) 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. Each smart pointer stores a reference counter, i.e. how many smart pointers are pointing to the managed object. Reference counting is thread-safe, as modifying the counter is an atomic operation.在 C++ API 类的 UNIGINE 实例中(例如:Node, Mesh, Body, Image 等...)只存储指向内部 C++ 类实例的指针,不能通过标准的 new/delete 运算符创建和删除它们。所以它们应该被声明为允许你自动管理它们生命周期的智能指针 (Unigine::Ptr)。 UNIGINE 拥有自己优化的内存分配器,可实现更快、更高效的内存管理。每个智能指针存储一个引用计数器,即有多少智能指针指向管理对象。引用计数是线程安全的,因为修改计数器是一个原子操作。
Not all methods of the Engine's internal C++ classes are exposed to the user, some of them are used by the Engine only. These are specific functions that either are used only for some internal purposes, or cannot be given to the user "as is". So, to filter out such methods an intermediate level, called interface, is used. This interface stores a pointer to the instance of the Engine's internal C++ class.并非引擎内部 C++ 类的所有方法都向用户公开,其中一些仅由引擎使用。这些是仅用于某些内部目的或不能“按原样”提供给用户的特定功能。因此,为了过滤掉这样的方法,使用了一个称为interface的中间层。该接口存储了一个指向引擎内部 C++ 类实例的指针。
To create an instance of an internal class we should declare a smart pointer for it and call the create() method — class constructor — providing construction parameters if necessary.要创建内部类的实例,我们应该为它声明一个智能指针,并调用 create() 方法——类构造函数——必要时提供构造参数。
// instantiating an object of an internal class
<Class>Ptr instance = <Class>::create(<construction_parameters>);
Lifetime一生#
We can divide all objects into two groups based on the way their lifetime is managed:我们可以根据管理生命周期的方式将所有对象分为两组:
-
Ownership objects (Image, Texture, Mesh, Tileset, etc.) these objects are managed in accordance with reference counter: when the last smart pointer is destroyed, the counter goes to 0, and the managed object is then automatically deleted. 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). Ownership 对象(Image, Texture, Mesh, Tileset 等)这些对象是按照引用计数器管理的:当最后一个智能指针被销毁时,计数器转到0,然后被管理的对象被自动删除。在这种情况下,假设不再需要该对象(引擎对此一无所知,并且用户没有能够使用它的指针),因此将其删除。 (例如,在范围内声明的此类对象将在离开范围时自动删除)。
// 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;
Complete list of Ownership Objects:Ownership 对象的完整列表:
- Blob
- Camera
- Curve2d
- Dir
- Ellipsoid
- File
- GameIntersection
- Image
- ImageConverter
- Json
- LandscapeFetch
- LandscapeImages
- LandscapeMapFileCreator
- LandscapeMapFileSettings
- LandscapeTextures
- Mesh
- MeshDynamic
- MeshStatic
- ObjectIntersection
- ObjectIntersectionNormal
- ObjectIntersectionTexCoord
- PackageUng
- Path
- PathRouteIntersection
- PhysicsIntersection
- PhysicsIntersectionNormal
- RegExp
- RenderTarget
- Shader
- ShapeContact
- Socket
- Stream
- StructuredBuffer
- SystemDialog
- Texture
- TextureCurve
- TilesetFile
- UlonArg
- UlonNode
- UlonValue
- Viewport
- WorldIntersection
- WorldIntersectionNormal
- WorldIntersectionTexCoord
- Xml
-
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()). Non-Ownership 对象(节点、小部件、材料、属性等)——这些对象与引擎交互并从它们创建的那一刻起就被引擎管理(它们参与主循环,可以通过名称等检索)。 )。这些对象的生命周期不是由引用计数器决定的,它们提供了弱引用的机制,所以你可以检查一个对象是否被删除。要删除此类对象,您应该使用 deleteLater() 或相应的管理器方法(例如:Materials::removeMaterial())。
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, 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).引擎管理——在这种情况下,节点将在引擎关闭时自动删除(可用于更改世界时应保留的节点)。
Upcasting and Downcasting上行和下行#
Sometimes (e.g. when we use World::getNodeByName(), etc. ) we get a NodePtr value, which is a pointer to the base class, but in order to perform operations with certain object (e.g. ObjectMeshDynamicPtr) we need to perform downcasting (i.e. convert from a pointer-to-base to a pointer-to-derived). The following methods were introduced:有时(例如,当我们使用 World::getNodeByName() 等时)我们会得到一个 NodePtr 值,它是一个指向基类的指针,但是为了对某些对象(例如 ObjectMeshDynamicPtr)执行操作,我们需要执行向下转换(即从指针转换-to-base 指向派生指针)。介绍了以下方法:
- static_ptr_cast — static casting without any checks (in accordance with C++ semantics)static_ptr_cast — 没有任何检查的静态转换(符合 C++ 语义)
- checked_ptr_cast — downcasting with automatic type checking performedchecked_ptr_cast — 执行自动类型检查的向下转型
- dynamic_ptr_cast — dynamic casting (in accordance with C++ semantics)dynamic_ptr_cast — 动态转换(符合 C++ 语义)
Sometimes you may also need to perform upcasting (i.e. convert from a pointer-to-derived to a pointer-to-base) this type of casting is performed automatically.有时您可能还需要执行向上转换(即从指向派生的指针转换为指向基的指针),这种类型的转换是自动执行的。
The code samples below demonstrate the points described above.下面的代码示例演示了上述要点。
Example 1示例 1
#include <UnigineEditor.h>
using namespace Unigine;
/* .. */
// find a pointer to node by a given name
NodePtr baseptr = World::getNodeByName("my_meshdynamic");
// cast a pointer-to-derived from pointer-to-base with automatic type checking
ObjectMeshDynamicPtr derivedptr = checked_ptr_cast<ObjectMeshDynamic>(baseptr);
// static cast: pointer-to-derived (File) from pointer-to-base (Stream)
if(stream->getType() == Stream::FILE)
FilePtr file = static_ptr_cast<File>(stream);
// upcast to the pointer to the Object class which is a base class for ObjectMeshDynamic
ObjectPtr object = derivedptr;
// upcast to the pointer to the Node class which is a base class for all scene objects
NodePtr node = derivedptr;
Deleting Objects删除对象#
A smart pointer has the clear() destructor intended for ownership objects clearing the pointer and deleting the object only in case if the smart pointer calling this method is the last one pointing to the object (interface, in this case). This should be taken into account.智能指针具有用于 ownership 对象的 clear() 析构函数,仅在调用此方法的智能指针是最后一个指向对象(在本例中为interface )的情况下清除指针并删除对象。应该考虑到这一点。
As for non-ownership objects, they can be deleted via one of the following methods:对于 non-ownership 对象,可以通过以下方法之一删除它们:
- deleteLater() — performs delayed deletion, in this case the object will be deleted during the next swap() stage of the main loop (rendering of the object ceases immediately, but it still exists in memory for a while, so you can get it from its parent, for example). This method simplifies object deletion from a secondary thread, so you can call it and forget about the details, letting the Engine take control over the process of deletion, which can be used for future optimizations;deleteLater() — 执行延迟删除,在这种情况下,对象将在主循环的下一个 swap() 阶段被删除(对象的渲染立即停止,但它仍然存在于内存中一段时间,因此您可以从其父级获取它,例如)。该方法简化了从辅助线程删除对象的过程,你可以调用它而忘记细节,让引擎控制删除的过程,这可以用于以后的优化;
- deleteForce() — performs immediate deletion, which might be necessary in some cases. Calling this method for main-loop-dependent objects (e.g., nodes) is safe only when performed from the Main thread.deleteForce() — 执行立即删除,这在某些情况下可能是必要的。仅当从主线程执行时,为主循环相关对象(例如,节点)调用此方法是安全的。
Both these methods can be safely called more than once for a single object (as well as after an object has been deleted by the Engine) without causing a double deletion. So, no worries, call it whenever an object is no longer needed.对于单个对象(以及在引擎删除对象后),可以安全地多次调用这两种方法,而不会导致双重删除。所以,不用担心,只要不再需要对象就调用它。