UE4 智慧指標

天才寶藏發表於2020-10-27

智慧指標庫:減輕記憶體分配與追蹤負擔 ,C++11中智慧指標的定義實現

  • 共享指標TSharedPtr
  • 共享引用 TSharedRef(不可為空)
  • 弱指標 TWeakPtr
  • 助手類與函式

助手類與函式

  • MakeShareable()-用於從C ++指標初始化共享指標(啟用隱式轉換)
  • TSharedFromThis-您可以從this派生自己的類,以從“ this”獲取TSharedRef
  • StaticCastSharedRef()-靜態轉換實用工具函式,通常用於向下轉換為派生型別。
  • ConstCastSharedRef()-將“ const”引用轉換為“可變”智慧引用
  • StaticCastSharedPtr()-動態強制轉換實用程式函式,通常用於向下轉換為派生型別。
  • ConstCastSharedPtr()-將“ const”智慧指標轉換為“可變”智慧指標

使用共享引用TSahredRef和指標TSharedPtr等的好處

  • 乾淨的語法:可以像常規C ++指標一樣複製,取消引用和比較共享指標。
  • 防止記憶體洩漏: 物件在沒有共享引用的時候
  • 弱引用: 弱指標可以安全地檢查物件何時被破壞
  • 執行緒安全:可以從多個執行緒安全中訪問的“安全執行緒”
  • 無處不在: 可以建立指向幾乎任何物件型別的共享指標
  • 執行時安全: 共享引用永遠不會為null,並且始終可以取消引用
  • 沒有參考週期(ReferenceCycle): 使用弱指標來破壞參考週期
  • 賦予意圖:可以輕鬆地從觀察者告訴物件所有者owner
  • 效能: 共享指標具有最小的開銷,所有操作都是恆定時間
  • 強大的功能:支援’const’,將宣告轉發給不完整的型別,型別轉換等
  • 記憶體:只有64位C ++指標大小的兩倍(加上共享的16位元組引用控制器)

侷限性:

  • 共享指標與虛幻物件(UObject類)不相容!
  • 當前僅具有常規解構函式的型別(無自定義刪除器)
  • 目前尚不支援動態分配的陣列(例如MakeSharable(new int32 [20])
  • 尚不支援將TSharedPtr / TSharedRef隱式轉換為bool和(boost:shared_ptrstd :: shared_ptr)之類的區別:
  • 型別名稱和方法名稱與Unreal的程式碼庫更加一致
  • 必須使用Pin()將弱指標轉換為共享指標(無顯式建構函式)
    -執行緒安全功能是可選的,而不是強制的
  • TSharedFromThis返回共享的參考,而不是共享的指標
  • 省略了某些功能(例如use_count(),unique()等)
  • 不允許例外(已省略所有相關功能)
  • 尚不支援自定義分配器和自定義刪除功能

使用Tips:

  • 儘可能使用TSharedRef代替TSharedPtr-永遠不能為nullptr
  • 可以呼叫TSharedPtr :: Reset()`釋放對物件的引用(並可能取消分配)
  • 使用MakeShareable()輔助函式隱式轉換為TSharedRefsTSharedPtrs
  • 永遠不能重置TSharedRef或將其分配給nullptr,但是可以為其分配新物件
  • 共享的指標假定物件的所有權-無需呼叫delete self
  • 通常,在將C ++指標傳遞給新的共享指標時,應該“操作新的”
  • 將智慧指標作為函式引數而不是TWeakPtr傳遞時,請使用TSharedRefTSharedPtr
  • 智慧指標的“執行緒安全”版本要慢一些-僅在需要時使用它們
  • 可以轉發宣告不完整型別的共享指標,就像您期望的那樣!
  • 相容型別的共享指標將隱式轉換(例如,向上轉換)
  • 可以為TSharedRef <MyClass>建立typedef以使其更容易鍵入
  • 為了獲得最佳效能,儘量減少對TWeakPtr :: Pin的呼叫(或對TSharedRef /TSharedPtr的轉換)
  • 如果類繼承TSharedFromThis,這個類可以將自己作為共享引用返回
  • 要向下轉換指向派生物件類的指標,請指向StaticCastSharedPtr函式
  • 共享指標完全支援'const'物件!
  • 可以使用ConstCastSharedPtr函式使'const'共享指標可變

下面是 SharedPointerTesting.inl檔案中的一些使用的UE4智慧指標的測試示例

inl檔案:inl檔案是行內函數的原始檔。
行內函數通常在C++標頭檔案中實現,但是當C++標頭檔案中行內函數過多的情況下,
我們想使標頭檔案看起來簡潔點,像普通函式那樣將行內函數宣告和函式定義放在標頭檔案和實現檔案中,
具體做法將是:將行內函數的具體實現放在inl檔案中,然後在該標頭檔案末尾使用#include引入該inl檔案。

/*
"->"箭頭和"(*)."對於智慧指標指向的物件的修改都是有效的,
對於多個智慧指標指向的物件,釋放其中一個智慧指標,其指向的物件並不會被銷燬
*/
{
	// Test arrow operator
	struct FSharedTest
	{
    	bool bFoo;
	};
	TSharedPtr< FSharedTest, Mode > SharedArray( new FSharedTest() );
	SharedArray->bFoo = true;	
	// Test dereference operator
	( *SharedArray ).bFoo = false;	
	// Create an additional reference to an existing shared pointer
	TSharedPtr< FSharedTest, Mode > OtherSharedArrayReference( SharedArray );
	// Release original reference (object should not be destroyed)
	SharedArray.Reset();//釋放其中一個智慧指標,其指向的物件並不會被銷燬
	// NOTE: OtherSharedArrayReference goes out of scope here (object is destroyed)
 }

/*
    這幾個智慧指標指向的物件實際上還是同一個物件,但是能獲取到物件的值根據智慧指標的類來決定
*/
{
    // Test casting
    class FBase
    {
    	bool bFoo;
	};
	class FDerived
	: public FBase
	{ };
     {
        // Explicit downcast to derived shared ptr 顯式的向下轉換為派生類的智慧指標
    	TSharedPtr< FBase, Mode > DerivedAsBasePtr( new FDerived() );
    	TSharedPtr< FDerived, Mode > DerivedPtr( StaticCastSharedPtr< FDerived >( DerivedAsBasePtr ) );
      }
      {
        // Initialize base from derived (implicit upcast) 由派生類隱式的向上轉換初始化基類
    	 TSharedPtr< FDerived, Mode > DerivedPtr( new FDerived() );
    	 TSharedPtr< FBase, Mode > BasePtr( DerivedPtr );
      }
      {
    	// Assign derived to base (implicit upcast)由派生類隱式的向上轉換分配基類
    	TSharedPtr< FDerived, Mode > DerivedPtr( new FDerived() );
    	TSharedPtr< FBase, Mode > BasePtr = DerivedPtr;		
       }
}
// Test 'const'
/*
總結:
同型別的智慧指標物件可以進行比較,型別不同不能作比較(例如int32和float)
分配const指標(僅引用,可以!)

指向const物件的智慧指標不能隱式轉換為沒有const標記的智慧指標,需要顯式的呼叫ConstCastSharedPtr()
ConstCastSharedPtr()就是將“const”智慧指標轉變為“mutable”智慧指標
“mutable”的智慧指標可以直接分配給“const”型別的智慧指標
舉例:
ConstPtrA=ConstPtrB                                // √ 分配const指標
ConstPtrA=MutablePtrC                              // √ 分配const指標
MutablePtrC=ConstPtrB                              // X  不可以隱式的Const_Cast轉變
MutablePtrC=ConstCastSharedPtr<float>(ConstPtrB)   // √ 顯式的呼叫ConstCastSharedPtr(),可以將“const”智慧指標轉變為“mutable”智慧指標

/*
關於TSharedPtr和TShareRef
*/
*************************TSharedPtr*************************
//空物件的 智慧指標 TSharedPtr
TSharedPtr< float, Mode > FloatPtr;//FloatPtr.IsValid()為flase,FloatPtr.Get()為nullptr
TSharedPtr< float, Mode > FloatPtrA = nullptr;
TSharedPtr< float, Mode > FloatPtrA(nullptr);

//給值 TSharedPtr
TSharedPtr< float, Mode > FloatPtr( new float( 1.0f ) );
TSharedPtr< float, Mode > FloatPtr = MakeSharable( new float( 1.0f ) );
TSharedPtr< float, Mode > FloatPtr = new float( 1.0f ); //錯誤
TSharedPtr< float, Mode > FloatPtr = float( 1.0f ); //錯誤
TSharedPtr< float, Mode > FloatPtr (float( 1.0f ));//錯誤
 
//TSharedPtr之間的
TSharedPtr< float, Mode > FloatPtrA = FloatPtr;//這兩個智慧指標指向同一物件
TSharedPtr< float, Mode > FloatPtrB(FloatPtrA);//FloatPtrB和FloatPtrA智慧指標指向同一個物件

//從TSharedRef到TSharedPtr
TSharedPtr< float, Mode > FloatPtrB(FloatRef);
TSharedPtr< float, Mode > FloatPtrB = FloatRef;

*************************TSharedRef*************************
TSharedRef< float, Mode > FloatRef( new float( 1.0f ) );
TSharedRef< float, Mode > FloatRef = MakeShareable( new float( 123.0f ) );
TSharedRef< float, Mode > FloatRef;//錯誤,TSharedRef沒有預設構造,不會編譯

//TSharedRef沒有隱式建構函式來表示指標(包括nullptr),需要ToSharedRef()函式
TSharedRef< float, Mode > FloatRef = nullptr;//錯誤
TSharedRef< float, Mode > FloatRef = FloatPtr;//錯誤
TSharedRef< float, Mode > FloatRef(FloatPtr)//錯誤
TSharedRef< float, Mode > FloatRef(floatPtr.ToSharedRef());//注意:FloatPtr需要不能為nullptr,否則執行的時候會斷言包錯
TSharedRef< float, Mode > FloatRef = floatPtr.ToSharedRef();//注意:FloatPtr需要不能為nullptr,否則執行的時候會斷言包錯

// TSharedFromThis
		{
			class FMyClass
				: public TSharedFromThis< FMyClass, Mode >
			{
			public:
				TSharedRef< FMyClass, Mode > GetSelfAsShared()
				{
					return AsShared();//返回的是FMyClass的物件作為TSharedRef(注意:注意這個物件需要已建立例項,且不在物件的解構函式中呼叫)
				}
			};

			// Grab shared pointer to stack-allocated class, before ever assigning it to
			// a shared pointer reference.  This will trigger an assertion!
			if( 0 )//錯誤的:因為MyClass這個物件沒有建立這個物件的例項,在AsShared()中會觸發斷言
			{
				FMyClass MyClass;
				TSharedRef< FMyClass, Mode > TheClassPtr( MyClass.GetSelfAsShared() );
			}

			TSharedPtr< FMyClass, Mode > TheClassPtr1( new FMyClass() );
			{
				FMyClass* MyClass = TheClassPtr1.Get();
				TSharedRef< FMyClass, Mode > TheClassPtr2( MyClass->GetSelfAsShared() );
			}
		}

相關文章