[UE4] TSharedPtr, TWeakObjectPtr and TUniquePtr
TSharedPtr
, TWeakObjectPtr
and TUniquePtr
imitated from shared_ptr
, weak_ptr
and unique_ptr
of C++11
.
TSharedPtr
Summary
TSharedPtr
is the counter of reference of object. The count that TSharedPtr
reference to would increase one while TSharedPtr
was assigned once, the object would be destroyed when the count of reference is 0.
Usage:
TSharedPtr<TestClass> ObjPtr = MakeShareable(new TestClass());
Objects would never be destroyed if two TSharedPtr
, which are reference to a same object, were assigned to each other, resulting a memory leak.
Whether if TSharedPtr works for UObject or not
As the comments of TSharedPtr
say, it only works for raw pointers
:
/** Proxy structure for implicitly converting raw pointers to shared/weak pointers */
// NOTE: The following is an Unreal extension to standard shared_ptr behavior
template< class ObjectType >
struct FRawPtrProxy
{
/** The object pointer */
ObjectType* Object;
/** Reference controller used to destroy the object */
FReferenceControllerBase* ReferenceController;
/** Construct implicitly from an object */
FORCEINLINE FRawPtrProxy( ObjectType* InObject )
: Object ( InObject )
, ReferenceController( NewDefaultReferenceController( InObject ) )
{
}
/** Construct implicitly from an object and a custom deleter */
template< class Deleter >
FORCEINLINE FRawPtrProxy( ObjectType* InObject, Deleter&& InDeleter )
: Object ( InObject )
, ReferenceController( NewCustomReferenceController( InObject, Forward< Deleter >( InDeleter ) ) )
{
}
};
In theory, Unreal GC system is to UObject
what std::shared_ptr<>
is to raw pointer, so TSharedPtr
doesn’t need to work for UObject
.
But, there’s no limitation for counting UObject
in the source code of SharedPointerInternals.h
in engine.
If a UObject
was passed as parameter into TSharedPtr
:
void ATestPlayerController::TestFun()
{
TestActor = NewObject<AActor>(this);
TSharedPtr<AActor> SharePtr2 = MakeShareable(TestActor);
}
Application would crash after jumping out of function TestFun
:
Assertion failed: GetFName() == NAME_None [File:D:/Build/++UE4/Sync/Engine/Source/Runtime/CoreUObject/Private/UObject/UObjectBase.cpp] [Line: 136]
UE4Editor_Core!AssertFailedImplV() [d:\build\++ue4\sync\engine\source\runtime\core\private\misc\assertionmacros.cpp:105]
UE4Editor_Core!FDebug::CheckVerifyFailedImpl() [d:\build\++ue4\sync\engine\source\runtime\core\private\misc\assertionmacros.cpp:455]
UE4Editor_CoreUObject!UObjectBase::~UObjectBase() [d:\build\++ue4\sync\engine\source\runtime\coreuobject\private\uobject\uobjectbase.cpp:136]
UE4Editor_Engine!AActor::`vector deleting destructor'()
UE4Editor_TestTD4_Win64_DebugGame!SharedPointerInternals::TReferenceControllerWithDeleter<AActor,SharedPointerInternals::DefaultDeleter<AActor> >::DestroyObject() [D:\Program_Files\Epic Games\UE_4.24\Engine\Source\Runtime\Core\Public\Templates\SharedPointerInternals.h:116]
UE4Editor_TestTD4_Win64_DebugGame!TSharedPtr<AActor,0>::~TSharedPtr<AActor,0>()
UE4Editor_TestTD4_Win64_DebugGame!ATestPlayerController::TestFun() [D:\workspace\unreal_dev\TestTD4\Source\TestTD4\TestTD4PlayerController.cpp:124]
As shown above: If TSharedPtr
reference to UObject
, it works at compiling, initialization of TSharedPtr
also works at run-time, but it crashed on destruction.
How to add raw pointers into GC of Unreal
If want to add raw pointers into GC system of UE4, raw class must inherits from FGCObject
.
TWeakObjectPtr
Summary
The memory presented by TWeakObjectPtr
can’t be prevented from garbage collecting. Once the object that was presented by TWeakObjectPtr
was destroyed in other place, the inner pointer of TWeakObjectPtr
would be assigned as nullptr
automatically, and TWeakObjectPtr::IsValid()
would return false
. But TSharedPtr
has no features of TWeakObjectPtr
.
Usage
Assignment
TWeakObjectPtr<AActor> MyWeakActor;
MyWeakActor = MyActor;
Get value
AActor* Actor = MyWeakActor.Get();
or
if(MyWeakActor.Get())
{
ACharacter* Character = Cast<ACharacter>(MyWeakActor);
}
if MyActor
has been destroyed, MyWeakActor.Get()
would return nullptr
MyActor->Destroy();
bool IsValid = MyWeakActor.Get() != nullptr; //false
if MyActor
has been recycled by GC, Cast<AMyCharacter>(MyActor)
would cause crash after a while, but Cast<AMyCharacter>(MyWeakActor)
would not.
Remove in Array
Examples:
APawn* TestPawn = GetWorld()->SpawnActor<APawn>(MyPawnClass, FVector(100.f, 100.f, 0.f), FRotator::ZeroRotator);
APawn* MyPawn = GetWorld()->SpawnActor<APawn>(MyPawnClass, FVector(200.f, 200.f, 0.f), FRotator::ZeroRotator);
TestArray.Add(TWeakObjectPtr<APawn>(TestPawn));
TestArray.Add(TWeakObjectPtr<APawn>(MyPawn));
int Num = TestArray.Num(); // 2
TWeakObjectPtr<APawn> WeakPtr1(TestPawn);
TestArray.Remove(WeakPtr1);
int Num = TestArray.Num(); // 1
TWeakObjectPtr<APawn> WeakPtr2(TestPawn);
TestArray.Remove(WeakPtr2);
int Num2 = TestArray.Num(); // 1
TWeakPtr
Difference between TWeakPtr and TWeakObjectPtr
TWeakObjectPtr works for weak pointers of UObjects, TWeakPtr works for pointers to everything else.
Since UObjects are garbage collected and shared pointers are reference counted, we cannot have the same weak pointer type for all, unfortunately.
Difference between TWeakPtr and TWeakObjectPtr?
https://answers.unrealengine.com/questions/298868/difference-between-tweakptr-and-tweakobjectptr.html
Convert TWeakPtr to TSharedPtr
Example from Engine\Source\Runtime\MediaAssets\Private\Misc\MediaTextureResource.cpp
:
TSharedPtr<FMediaTextureSampleSource, ESPMode::ThreadSafe> SampleSource = Params.SampleSource.Pin();
TUniquePtr (Unique Pointers)
Summary
A Unique Pointer solely and explicitly owns the object it references. Since there can only be one Unique Pointer to a given resource, Unique Pointers can transfer ownership, but cannot share it. Any attempts to copy a Unique Pointer will result in a compile error. When a Unique Pointer is goes out of scope, it will automatically delete the object it references.
TUniquePtr
works for Non-UObject, there’s compilation error while initializing TUniquePtr
with UObject
.
Example : Basic
Define:
TUniquePtr<FMeshTriOctree> MTOctree;
Instantiate:
MTOctree = MakeUnique<FMeshTriOctree>(
Bounds.GetCenter(),
Bounds.GetExtent().GetMax()
);
Example : TUniquePtr & TQueue
Add element:
class FTestStruct
{
};
TQueue<TUniquePtr<FTestStruct>> TestQueue;
TUniquePtr<FTestStruct> Ptr = MakeUnique<FTestStruct>();
TestQueue.Enqueue(MoveTemp(Ptr));
If forget to invoke MoveTemp
, e.g.:
TQueue<TUniquePtr<FTestStruct>> TestQueue;
TUniquePtr<FTestStruct> Ptr = MakeUnique<FTestStruct>();
TestQueue.Enqueue(Ptr);
You would get error:
D:\UE_4.22\Engine\Source\Runtime\Core\Public\Containers/Queue.h(252): error C2248: 'TUniquePtr<FTestStruct,TDefaultDelete<T>>::TUniquePtr': cannot access private member declared in class 'TUniquePtr<FTestStruct,TDefaultDelete<T>>'
2> with
2> [
2> T=FTestStruct
2> ]
Remove element:
1st way:
TUniquePtr<FTestStruct> Ptr = MoveTemp(TestQueue.Peek());
TestQueue.pop();
2nd way (recommended) :
TUniquePtr<FTestStruct> Buffer;
TestQueue.Dequeue(Buffer)
Example : TUniquePtr & TArray
Add Element:
TUniquePtr<FTestStruct> Ptr = MakeUnique<FTestStruct>();
TestArray.Add(MoveTemp(Ptr));
Remove element:
1st way:
TUniquePtr<FTestStruct> Ptr = MoveTemp(TestArray[TestArray.Num() - 1]);
TestArray.RemoveAt(TestArray.Num() - 1);
2nd way (recommended) :
TUniquePtr<FTestStruct> Ptr = TestArray.Pop();
Issues
If there’s FString
varible in class and want to create TUniquePtr
using this class, Move Constructor
need to be provided in class.
Example:
Engine\Source\Runtime\Online\WebSockets\Private\Lws\LwsWebSocket.h
struct FLwsReceiveBufferText
{
/**
* Constructor
* @param InText The packet contents
*/
FLwsReceiveBufferText(FString&& InText) : Text(MoveTemp(InText))
{
}
/** Text packet received */
const FString Text;
};
typedef TUniquePtr<FLwsReceiveBufferText> FLwsReceiveBufferTextPtr;
Otherwise you will get a compilication error upon using your class as a template parameter (e.g. TQueue<UniquePtr<FTestStruct>>
):
D:\UE_4.22\Engine\Source\Runtime\Core\Public\Containers/Queue.h(252): error C2248: 'TUniquePtr<FTestStruct,TDefaultDelete<T>>::TUniquePtr': cannot access private member declared in class 'TUniquePtr<FTestStruct,TDefaultDelete<T>>'
2> with
2> [
2> T=FTestStruct
2> ]
2> D:\UE_4.22\Engine\Source\Runtime\Core\Public\Templates/UniquePtr.h(305): note: see declaration of 'TUniquePtr<FTestStruct,TDefaultDelete<T>>::TUniquePtr'
2> with
2> [
2> T=FTestStruct
2> ]
2> D:\workspace\unreal_dev\TestProj\Source\Public\TestClass.h(73): note: see declaration of 'TUniquePtr<FTestStruct,TDefaultDelete<T>>'
2> with
2> [
2> T=FTestStruct
2> ]
Reference
unreal-unique-pointer
https://baemincheon.github.io/2020/03/14/unreal-unique-pointer/
UE4 TSharedPtr和UObject的垃圾回收
http://www.v5xy.com/?p=808
There’s a Huge Difference, One Will Always Crash
https://answers.unrealengine.com/questions/48818/whats-the-difference-between-using-tweakobjectptr.html
what is a “weak object pointer”?
https://answers.unrealengine.com/questions/201186/what-is-a-weak-object-pointer.html
Unreal Smart Pointer Library
https://docs.unrealengine.com/en-US/Programming/UnrealArchitecture/SmartPointerLibrary/index.html
已识乾坤大,犹怜草木青。长空送鸟印,留幻与人灵。--- 马一浮《旷怡亭口占》