智能指针

UE源码阅读

Posted by Bob on April 22, 2025

内存大小

TSharedRef维护一个指针对象和一个引用计数控制器, 在 64 位系统中只有 C++ 指针大小的两倍(加上一个共享的 16 字节引用控制器)。


template< class ObjectType, ESPMode InMode >
class TSharedRef
{
    ObjectType* Object;
    SharedPointerInternals::FSharedReferencer< Mode > SharedReferenceCount;
}

template< ESPMode Mode >
class FSharedReferencer
{
    TReferenceControllerBase<Mode>* ReferenceController;
}

template <ESPMode Mode>
class TReferenceControllerBase
{
    using RefCountType = std::conditional_t<Mode == ESPMode::ThreadSafe, std::atomic<int32>, int32>;
    RefCountType SharedReferenceCount{1};
    RefCountType WeakReferenceCount{1};
}

enum class ESPMode : uint8
{
	/** Forced to be not thread-safe. */
	NotThreadSafe = 0,

	/** Thread-safe, never spin locks, but slower */
	ThreadSafe = 1
};

引用计数

  • SharedReferenceCount默认是1,因为创建即为1次了。
  • Copy constructor 计数会+1
  • Move constructor 计数转移给新的,然后自己计数清空
  • Destructor 计数-1,如何计数为0了调用DestroyObject
  • Assignment operator 新的+1,自己-1
  • Move assignment operator 自己接受新的计数,自己原来持有那份老的-1

替换引用


    //Assignment operator replaces this shared reference with the specified shared reference.
	FORCEINLINE TSharedRef& operator=( TSharedRef const& InSharedRef )
	{
		TSharedRef Temp = InSharedRef;
		::Swap(Temp, *this);
		return *this;
	}

    FORCEINLINE TSharedPtr& operator=( TSharedPtr&& InSharedPtr )
	{
		if (this != &InSharedPtr)
		{
			Object = InSharedPtr.Object;
			InSharedPtr.Object = nullptr;
			SharedReferenceCount = MoveTemp(InSharedPtr.SharedReferenceCount);
		}
		return *this;
	}

    template <typename T>
    struct TUseBitwiseSwap
    {
        // We don't use bitwise operations for 'register' types because this will force them into memory and be slower.
        enum { Value = !(std::is_enum_v<T> || std::is_pointer_v<T> || std::is_arithmetic_v<T>) };
    };

    /**
     * Swap two values.  Assumes the types are trivially relocatable.
     */
    template <typename T>
    inline void Swap(T& A, T& B)
    {
        if constexpr (TUseBitwiseSwap<T>::Value)
        {
            TTypeCompatibleBytes<T> Temp;
            *(TTypeCompatibleBytes<T>*)&Temp = *(TTypeCompatibleBytes<T>*)&A;
            *(TTypeCompatibleBytes<T>*)&A    = *(TTypeCompatibleBytes<T>*)&B;
            *(TTypeCompatibleBytes<T>*)&B    = *(TTypeCompatibleBytes<T>*)&Temp;
        }
        else
        {
            T Temp = MoveTemp(A);
            A = MoveTemp(B);
            B = MoveTemp(Temp);
        }
    }

    /**
     * MoveTemp will cast a reference to an rvalue reference.
     * This is UE's equivalent of std::move except that it will not compile when passed an rvalue or
     * const object, because we would prefer to be informed when MoveTemp will have no effect.
     */
    template <typename T>
    UE_INTRINSIC_CAST FORCEINLINE constexpr std::remove_reference_t<T>&& MoveTemp(T&& Obj) noexcept
    {
        using CastType = std::remove_reference_t<T>;

        // Validate that we're not being passed an rvalue or a const object - the former is redundant, the latter is almost certainly a mistake
        static_assert(std::is_lvalue_reference_v<T>, "MoveTemp called on an rvalue");
        static_assert(!std::is_same_v<CastType&, const CastType&>, "MoveTemp called on a const object");

        return (CastType&&)Obj;
    }

限制

  • 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. MakeShareable( new int32[20] ))