UE4 的 TSharedPtr、TWeakObjectPtr 模仿自 C++11 的 shared_ptr 、 weak_ptr

TSharedPtr

Summary

TSharedPtr 相当于对象的引用计数器。每当对 TSharedPtr 赋值一次,该 TSharedPtr 引用对象计数加一,当引用计数为0时,则该对象被自动销毁。

用法:

TSharedPtr<TestClass> ObjPtr = MakeShareable(new TestClass());

如果两个 TSharedPtr 相互赋值,则会导致对象永不释放,导致内存泄漏。

TSharedPtr 是否对 UObject 对象计数

TSharedPtr的代码注释注明其作用对象为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 ) ) )
    {
    }
};

按理说:UE4的GC系统其实就相当于针对UObject类型对象的std::shared_ptr<>,所以TSharedPtr作用范围也没必要包含UObject
但是引擎源码SharedPointerInternals.h逻辑中,并没有看到不对UObject类型计数的逻辑。
但是如果你使用TSharedPtrUObject类型对象引用:

void ATestPlayerController::TestFun()
{
    TestActor = NewObject<AActor>(this);
    TSharedPtr<AActor> SharePtr2 = MakeShareable(TestActor);
}

当跳出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]

就是说:使用TSharedPtrUObject类型对象引用,编译正常,运行时TSharedPtr初始化也正常,但是当TSharedPtr被析构时就会导致程序崩溃。

如何将raw pointers加入UE4 GC系统

如果raw pointers想加入GC,那么指针所指对象必须继承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 like 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.h:

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 
{
    public:

};

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:\Program Files\Epic Games\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 method:

TUniquePtr<FTestStruct> Ptr = MoveTemp(TestQueue.Peek());
TestQueue.pop();

2nd method (recommended) :

TUniquePtr<FTestStruct> Buffer;
TestQueue.Dequeue(Buffer)
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:\Program Files\Epic Games\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:\Program Files\Epic Games\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


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