之前的博客《实现一个最基础的智能指针》里实现了一个最简单的使用引用计数方式的智能指针。而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.)
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
-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()
助手函数来隐式地将指针转换为TSharedRef
或TSharedPtr
。TSharedRef
重置为空,但你可以将它指定为一个新的对象。delete
了。new
来将一个C++指针传递给一个新的智能指针。TSharedRef
或TSharedPtr
,而不是TWeakPtr
。TSharedRef< MyClass >
使用typedef
来让表达更简洁。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
)TSharedPtr
或TSharedRef
转换为bool是不支持的。-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
Pin()
操作来将一个弱指针转换为一个共享指针(没有显式的构造函数)。TSharedFromThis
返回的是一个共享引用,而不是共享指针。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
)都还没有在所有平台上都可用。MakeShareable
和赋空值。使用TSharedPtr
的目的,正如之前在《实现一个最基础的智能指针》所讨论的,是想要用引用计数的方式来维护一个对象的销毁。
class FTestClass
{
};
可以使用MakeShareable
将一个普通C++指针转换为智能指针,这将意味着它指向的对象将会被智能指针的机制管理,在引用计数为0时自动销毁。
TSharedPtr<FTestClass> test = MakeShareable(new FTestClass());
需要注意的是,TSharedPtr
不能对UObject
类使用,因为UObject
自己已经有GC的机制了,不能再将其加入另一个内存管理的机制。
虽然上面讨论了弱指针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
的弱指针/**
* 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 thereGet()
能得到一个非空且有效的指针。当然,TWeakObjectPtr
只能和UObject
类配合,否则便会报错:
error C2338: TWeakObjectPtr can only be constructed with UObject types