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


已识乾坤大,犹怜草木青。长空送鸟印,留幻与人灵。--- 马一浮《旷怡亭口占》