目錄
概念
聲明和初始化
轉換為共享指針
打破循環引用
弱指針使用警告
概念
????????在UE C++ 中,弱指針(TWeakPtr
?)也是一種智能指針類型,主要用于解決循環引用問題以及在不需要強引用保證對象始終有效的場景下,提供一種可以獲取對象訪問權限的方式。與共享指針(TSharedPtr
?)和共享引用(TSharedRef
?)不同,弱指針不會增加其所指向對象的引用計數,這意味著它不會對對象的生命周期產生維持作用,即不會阻止對象被銷毀。
????????例如,在一些復雜的對象關系結構中,多個對象之間可能相互引用,如果都使用強引用(如?TSharedPtr
?或?TSharedRef
?),很容易形成循環引用,導致對象的引用計數永遠無法降為 0,從而造成內存泄漏。而弱指針可以在這種情況下參與對象的引用關系構建,避免出現循環引用問題,同時又能在對象仍然存在時,有機會獲取到對象的有效訪問權限。
????????在訪問弱指針引用的對象前,應使用?Pin
?函數生成共享指針。此操作確保使用該對象時其將繼續存在。如只需要確定弱指針是否引用對象,可將其與?nullptr
?比較,或在之上調用?IsValid
。
聲明和初始化
? ? ? ? 在如下代碼中,體現了弱指針不維持對象生命周期的特點,以及通過?Pin
?函數檢查對象是否還能獲取有效訪問權限的用法。
????????在第16行代碼中,使用?MakeShared<FMyStruct>()
?創建了一個?FMyStruct
?類型的對象,并通過?TSharedRef
?來管理這個對象,使得?ObjectOwnerRef
?指向新創建的對象。此時,該對象的引用計數被初始化為?1。
????????第17行代碼通過將?ObjectOwnerRef
?作為參數傳遞給?TWeakPtr
?的構造函數,創建了一個弱指針?ObjectObserver
,使其指向與?ObjectOwnerRef
?相同的?FMyStruct
?對象。需要注意的是,這個操作并不會增加對象的引用計數,對象的生命周期仍然僅由?ObjectOwnerRef
?(以及后續可能出現的其他指向該對象的共享指針或共享引用)來維持,ObjectObserver
?只是建立了一個對該對象的弱引用關系,用于后續在不影響對象生命周期的情況下嘗試獲取對對象的訪問權限。
????????第18行代碼創建了一個?TSharedPtr
?類型的共享指針?ObjectOwnerPtr
,并通過賦值操作讓它也指向?ObjectOwnerRef
?所指向的?FMyStruct
?對象。此時,對象的引用計數會從?1
(僅由?ObjectOwnerRef
?維持時)變為?2。
????????第19行代碼調用?ObjectOwnerPtr
?的?Reset
?函數,這會使得?ObjectOwnerPtr
?釋放對其所指向對象的強引用,對象的引用計數會相應地減?1
。在執行完這行代碼后,對象的引用計數變回?1
,僅由?ObjectOwnerRef
?來維持其生命周期。
????????第20~23行代碼使用了?弱指針ObjectObserver
?的?Pin
?函數來嘗試獲取一個指向原對象的臨時共享指針,以檢查對象是否仍然可以被訪問。Pin
?函數會在對象仍然存在(即對應的引用計數大于?0
?)的情況下,返回一個指向該對象的臨時?TSharedPtr
。
轉換為共享指針
??Pin
?函數將創建指向弱指針對象的共享指針。只要共享指針在范圍內且引用對象,則該對象將持續有效。
????????如下代碼主要展示了如何將一個由共享引用(TSharedRef
?)管理的對象轉換為可通過弱指針(TWeakPtr
?)來間接訪問的形式,并且演示了通過弱指針的?Pin
?操作獲取臨時共享指針(TSharedPtr
?),進而訪問對象成員函數?PrintAA
?的過程,整體體現了弱指針在不影響對象生命周期管理的情況下,實現對對象的安全訪問機制。
打破循環引用
出現循環引用的示例:
首先在“FMyStruct”結構體中定義一個共享指針?HoldPtr
,并初始化為?nullptr
然后創建兩個?FMyStruct
?類型的對象,并通過它們各自包含的?TSharedPtr<FMyStruct>
?類型成員變量?HoldPtr
?互相指向對方,形成了一個循環引用的結構,如下所示。
此時調用“LoopPtr”會發現,并沒有輸出析構的日志信息,說明產生了循環引用現象,導致對象的引用計數永遠無法降為 0。
為了打破循環引用,我們可以使用弱指針來代替共享指針
編譯后運行 結果如下,可以看到對象可以正常析構了:
弱指針使用警告
????????如不想保證數據對象會持續存在時,弱指針將非常有用,但該屬性可能會變得異常危險。在以下情況中請謹慎使用弱指針:
-
**在Set或Map中用作鍵。弱指針可能會在未通知容器的情況下隨時無效,因此共享指針或共享引用更適用于充當鍵。可安全地將弱指針用作數值。
-
雖然弱指針提供?
IsValid
?函數,但是檢查?IsValid
?無法保證對象在任何時間長度內均可持續有效。線程安全共享指針可能會因另一線程上的活動而隨時無效,因此使用線程安全共享指針應尤其注意。Pin
?返回的共享指針將使對象在代碼將其清除或其超出范圍前保持活躍狀態,因此?Pin
?函數是用于檢查的首選方法,此類檢查會導致取消引用或訪問存儲對象。
官方文檔地址:
https://dev.epicgames.com/documentation/zh-cn/unreal-engine/shared-references-in-unreal-engine?application_version=5.3