UE4智能指针学习记录

UE4智能指针

之前的博客《实现一个最基础的智能指针》里实现了一个最简单的使用引用计数方式的智能指针。而UE4也有一套智能指针的实现,可见《虚幻智能指针库 | Unreal Engine Documentation》中的介绍。

另外,我发现\Engine\Source\Runtime\Core\Public\Templates\SharedPointer.h中的注释也有很多有价值的信息,例如设计意图等,可以补充参考。(由于有些内容还没有较深的理解,不敢妄加翻译,先保留原文)

注释中的信息

This is a smart pointer library consisting of shared references (TSharedRef), shared pointers (TSharedPtr),weak pointers (TWeakPtr) as well as related helper functions and classes. This implementation is modeled after the C++0x standard library’s shared_ptr as well as Boost smart pointers.

这个智能指针库包括:“共享引用(TSharedRef)”“共享指针(TSharedPtr)”“弱指针(TWeakPtr)” 以及相关的助手函数和助手类。这个库是在C++0x标准库的shared_ptr以及Boost的智能指针之后实现的。

使用智能指针益处

Clean syntax. You can copy, dereference and compare shared pointers just like regular C++ pointers.
Prevents memory leaks. Resources are destroyed automatically when there are no more shared references.
Weak referencing. Weak pointers allow you to safely check when an object has been destroyed.
Thread safety. Includes “thread safe” version that can be safely accessed from multiple threads.
Ubiquitous. You can create shared pointers to virtually any type of object.
Runtime safety. Shared references are never null and can always be dereferenced.
No reference cycles. Use weak pointers to break reference cycles.
Confers intent. You can easily tell an object owner from an observer.
Performance. Shared pointers have minimal overhead. All operations are constant-time.
Robust features. Supports ‘const’, forward declarations to incomplete types, type-casting, etc.
Memory. Only twice the size of a C++ pointer in 64-bit (plus a shared 16-byte reference controller.)

  • 干净的语法结构。你可以“拷贝”,“解除”,“比较”一个共享指针,语法上就和常规的C++指针一样。
  • 避免内存泄漏。当没有其他引用时资源就会被自动销毁。
  • 弱引用:弱指针可以让你安全地检查一个对象是否被销毁了。
  • 线程安全:包含一个“线程安全型版本”可以安全地在多线程中访问。
  • Ubiquitous. You can create shared pointers to virtually any type of object.
  • 运行时安全性:共享引用永远不会为空,而且总是可以解除引用。
  • 避免循环引用:使用弱指针可以破坏引用循环。
  • Confers intent. You can easily tell an object owner from an observer.
  • 性能:共享指针都尽量使用了最小的封装,所有的操作都是“常量时间复杂度”。
  • 鲁棒性相关的特性:支持“const”,前向声明,类型转换等。
  • 内存:在64位上仅仅是C++指针的两倍(外加一个16位的引用计数器)

这个库包含了下面智能指针的定义

TSharedRef - Non-nullable, reference counted non-intrusive authoritative smart pointer
TSharedPtr - Reference counted non-intrusive authoritative smart pointer
TWeakPtr - Reference counted non-intrusive weak pointer reference

Tips

-Use TSharedRef instead of TSharedPtr whenever possible – it can never be nullptr!
- You can call TSharedPtr::Reset() to release a reference to your object (and potentially deallocate)
- Use the MakeShareable() helper function to implicitly convert to TSharedRefs or TSharedPtrs
- You can never reset a TSharedRef or assign it to nullptr, but you can assign it a new object
- Shared pointers assume ownership of objects – no need to call delete yourself!
- Usually you should “operator new” when passing a C++ pointer to a new shared pointer
- Use TSharedRef or TSharedPtr when passing smart pointers as function parameters, not TWeakPtr
- The “thread-safe” versions of smart pointers are a bit slower – only use them when needed
- You can forward declare shared pointers to incomplete types, just how you’d expect to!
- Shared pointers of compatible types will be converted implicitly (e.g. upcasting)
- You can create a typedef to TSharedRef< MyClass > to make it easier to type
- For best performance, minimize calls to TWeakPtr::Pin (or conversions to TSharedRef/TSharedPtr)
- Your class can return itself as a shared reference if you derive from TSharedFromThis
- To downcast a pointer to a derived object class, to the StaticCastSharedPtr function
- ‘const’ objects are fully supported with shared pointers!
- You can make a ‘const’ shared pointer mutable using the ConstCastSharedPtr function

  • 如果能用TSharedRef就不用TSharedPtr,因为TSharedRef可以保证值不是空的。
  • 你可以调用TSharedPtr::Reset()来解除对物体的引用(可能会导致物体的析构)。
  • 使用MakeShareable()助手函数来隐式地将指针转换为TSharedRefTSharedPtr
  • 你永远不能将TSharedRef重置为空,但你可以将它指定为一个新的对象。
  • 共享指针已经假定了对物体的所有权——因此就不需要再手动调用delete了。
  • 通常,你应该使用new来将一个C++指针传递给一个新的智能指针。
  • 当要向函数传递参数时,使用TSharedRefTSharedPtr,而不是TWeakPtr
  • “线程安全型”版本稍微慢一点——因此请仅在需要的时候才使用。
  • 你可以在智能指针中使用前向声明未完成的类。
  • Shared pointers of compatible types will be converted implicitly (e.g. upcasting)
  • 你可以对TSharedRef< MyClass >使用typedef来让表达更简洁。
  • 处于性能考虑,minimize calls to TWeakPtr::Pin (or conversions to TSharedRef/TSharedPtr)
  • 让你的类继承TSharedFromThis,你就可以return itself as a shared reference。
  • 为了向下转换成一个派生类的指针,需要用StaticCastSharedPtr函数。
  • const 是完全支持的!
  • 你可以用ConstCastSharedPtr函数来让一个const共享指针mutable

限制

-Shared pointers are not compatible with Unreal objects (UObject classes)!
- Currently only types with that have regular destructors (no custom deleters)
- Dynamically-allocated arrays are not supported yet (e.g. MakeSharable( new int32[20] ))
- Implicit conversion of TSharedPtr/TSharedRef to bool is not supported yet

  • 共享指针不支持UObject对象
  • 当前只支持那些有常规析构函数的类(没有自定义的deleter
  • 动态分配内存的数组是不支持的(例如: MakeSharable( new int32[20] )是不对的)
  • TSharedPtrTSharedRef转换为bool是不支持的。

和其他实现的区别(例如 boost:shared_ptr, std::shared_ptr)

-Type names and method names are more consistent with Unreal’s codebase
- You must use Pin() to convert weak pointers to shared pointers (no explicit constructor)
- Thread-safety features are optional instead of forced
- TSharedFromThis returns a shared reference, not a shared pointer
- Some features were omitted (e.g. use_count(), unique(), etc.)
- No exceptions are allowed (all related features have been omitted)
- Custom allocators and custom delete functions are not supported yet
- Our implementation supports non-nullable smart pointers (TSharedRef)
- Several other new features added, such as MakeShareable and nullptr assignment

  • 类名和操作名与UE4的代码更一致。
  • 你必须使用Pin()操作来将一个弱指针转换为一个共享指针(没有显式的构造函数)。
  • “线程安全型”这个特性是可选的,而不是强制的。
  • TSharedFromThis返回的是一个共享引用,而不是共享指针。
  • 有一些操作被忽略了(例如 use_count(),unique()等)
  • 不允许有exceptions(相关的操作都已经被忽略)。
  • 自定义的分配器以及自定义的delete函数都还不支持。
  • 我们的实现包含了一个非空的智能指针(指TSharedRef
  • 增加了一些新的特性:例如MakeShareable和赋空值。

为什么要实现我们自己的虚幻智能指针?

-std::shared_ptr (and even tr1::shared_ptr) is not yet available on all platforms
- Allows for a more consistent implementation on all compilers and platforms
- Can work seamlessly with other Unreal containers and types
- Better control over platform specifics, including threading and optimizations
- We want thread-safety features to be optional (for performance)
- We’ve added our own improvements (MakeShareable, assign to nullptr, etc.)
- Exceptions were not needed nor desired in our implementation
- We wanted more control over performance (inlining, memory, use of virtuals, etc.)
- Potentially easier to debug (liberal code comments, etc.)
- Prefer not to introduce new third party dependencies when not needed

  • std::shared_ptr(甚至是tr1::shared_ptr)都还没有在所有平台上都可用。
  • 在所有平台和编译器上有一个更加一致的实现。
  • 可以无缝与UE4的容器类等类对接。
  • 能针对平台特性(指线程,优化,等)有更好的控制。
  • 我们想要“线程安全型”这一特性是可选的(处于性能方面考虑)
  • 我们添加了我们自己的改进 ,例如MakeShareable和赋空值。
  • 在我们的实现中,exceptions是不需要且不被期望的。
  • 我们想要对性能有更好的控制(内联,内存,virtual的使用,等)
  • 潜在更容易调试 (liberal code comments, 等)
  • 我们倾向于不使用新的第三方依赖,除非真的需要。

使用 TSharedPtr

使用TSharedPtr的目的,正如之前在《实现一个最基础的智能指针》所讨论的,是想要用引用计数的方式来维护一个对象的销毁。

class FTestClass
{
};

可以使用MakeShareable将一个普通C++指针转换为智能指针,这将意味着它指向的对象将会被智能指针的机制管理,在引用计数为0时自动销毁。

TSharedPtr<FTestClass> test = MakeShareable(new FTestClass());

需要注意的是,TSharedPtr不能对UObject类使用,因为UObject自己已经有GC的机制了,不能再将其加入另一个内存管理的机制。

使用 TWeakObjectPtr

虽然上面讨论了弱指针TWeakPtr,但我目前还没有使用过它。

不过另外一种“弱指针”倒是经常使用:TWeakObjectPtr
使用它的目的是:有时候不确定一个UObject是否已经被某种原因被GC掉了,将其包裹进TWeakObjectPtr,则可用IsValid方法来确定它是否还有效。

/**
 * FWeakObjectPtr is a weak pointer to a UObject. 
 * It can return nullptr later if the object is garbage collected.
 * It has no impact on if the object is garbage collected or not.
 * It can't be directly used across a network.
 *
 * Most often it is used when you explicitly do NOT want to prevent something from being garbage collected.
 */
struct FWeakObjectPtr
  • FWeakObjectPtr是一个针对UObject的弱指针
  • 若对象已经被GC,则它可以返回nullptr
  • 它对于一个对象是否被GC并没有任何影响
  • 不能直接在网络中使用
  • 最常用的情况是:你并不想阻止一个物体被GC(但是还想要检测它是否被GC了)。
/**
 * TWeakObjectPtr is the templated version of the generic FWeakObjectPtr
 */
template<class T, class TWeakObjectPtrBase>

TWeakObjectPtrBase就是FWeakObjectPtr的模板版本。

/**  
 * Test if this points to a live UObject
 * @param bEvenIfPendingKill if this is true, pendingkill objects are considered valid
 * @param bThreadsafeTest if true then function will just give you information whether referenced
 *							UObject is gone forever (return false) or if it is still there (return true, no object flags checked).
 * @return true if Get() would return a valid non-null pointer
 */
FORCEINLINE bool IsValid(bool bEvenIfPendingKill, bool bThreadsafeTest = false) const
{
	return TWeakObjectPtrBase::IsValid(bEvenIfPendingKill, bThreadsafeTest);
}

/**
 * Test if this points to a live UObject. This is an optimized version implying bEvenIfPendingKill=false, bThreadsafeTest=false.
 * @return true if Get() would return a valid non-null pointer
 */
FORCEINLINE bool IsValid(/*bool bEvenIfPendingKill = false, bool bThreadsafeTest = false*/) const
{
	return TWeakObjectPtrBase::IsValid();
}
  • IsValid可以检测一个UObject是否“活着”
  • 参数bEvenIfPendingKill:如果是true,则pendingkill的对象将被视为有效,默认false。
  • 参数bThreadsafeTest:if true then function will just give you information whether referenced UObject is gone forever (return false) or if it is still there
  • 返回true则表示Get()能得到一个非空且有效的指针。

当然,TWeakObjectPtr只能和UObject类配合,否则便会报错:

error C2338: TWeakObjectPtr can only be constructed with UObject types

你可能感兴趣的:(UE4)