INTSourceChangelist:2621990 Availability:Public Title:アンリアル スマート ポインタ ライブラリ Crumbs:%ROOT%, Programming, Programming/UnrealArchitecture Description:弱い参照と null 非許容型な共有参照を含んだシェアード ポインタのカスタム実装 Version:4.9 [TOC(start:2)] ## スマート ポインタ [EXCERPT:Overview] **アンリアル スマート ポインタ ライブラリ** は、共有の参照 (**TSharedRef**)、シェアード ポインタ (**TSharedPtr**)、弱い参照 (**TWeakPtr**) はもちろん、関連するヘルパー関数とクラスを特別に実装したものです。この実装は C++0x 標準ライブラリの shared_ptr と Boost スマート ポインタを手本に実装しました。 | タイプ | 説明 | | --- | --- | | [](Programming/UnrealArchitecture/SmartPointerLibrary/SharedPointer) (TSharedPtr) | 参照カウント式、非侵入型の信頼できるスマート ポインタ。 | | [](Programming/UnrealArchitecture/SmartPointerLibrary/SharedReference) (TSharedRef) | null 非許容型、参照カウント式な非侵入型の信頼できるスマート ポインタ。 | | [](Programming/UnrealArchitecture/SmartPointerLibrary/WeakPointer) (TWeakPtr) | 参照カウント式、非侵入型の弱参照。 | [/EXCERPT:Overview] ### 参照権の共有とシェアード ポインタの利点 | 利点 | 説明 | | --- | --- | | Clean syntax (クリーンなシンタックス) | 通常の C++ ポインタと同じ要領でシェアード ポインタをコピー、ポインターの参照先の値の取得、また比較することができます。 | | Prevents memory leaks (メモリーリークの防止) | これ以上共有の参照が無い場合、リソースは自動的に破壊されます。| | Weak referencing (弱い参照) | オブジェクトが破壊された時、ウィーク ポインタで安全なチェックが可能になります。 | | Thread safety (スレッド セーフティ) | 複数のスレッドから安全なアクセスを可能にする「スレッドセーフ」なバージョンを実装しています。| | Ubiquitous (ユビキタス) | 事実上 どのような タイプのオブジェクトに対してもシェアード ポインタを作成できます。 | | Runtime safety (ランタイムの安全性) | 共有の参照は決して null にはならず、常にポインタから参照先の値を取得します。 | | No reference cycles (参照サイクルの破棄) | 参照サイクルの中断に弱い参照を使用します。 | | Confers intent | オブザーバー から簡単にオブジェクト オーナー を理解できます。 | | Performance (パフォーマンス) | シェアード ポインタはオーバーヘッドを最小限に抑えます。全ての操作は定数時間で行われます。 | | Robust features (堅牢な機能) | 不完全な型、タイプキャストに対して const や前方宣言をサポートします。 | | Memory | 64 ビット環境の C++ ポインタサイズの 2 倍程度です (加えて共有の 16 倍と参照コントローラー)。 | ### カスタム ライブラリを作成する理由 * std::shared_ptr (および tr1::shared_ptr) はまだ全てのプラットフォームに対応していません。 * 全てのコンパイラーやプラットフォームに対しより一貫性のある実装を有効にします。 * 他のアンリアル コンテナやタイプとシームレスに機能します。 * スレッドや最適化を含めてプラットフォームの特性を上手にコントロールします。 * スレッドセーフをオプション機能にします (パフォーマンスのため)。 * 独自の改善を追加しました (**MakeShareable**、NULL 値の割り当てなど)。 * この実装に例外は必要ありません。 * パフォーマンスのさらなるコントロールを強化しました (インライン化、メモリ、バーチャルメモリの使用など)。 * 潜在的により簡単なデバッグ作業 (進歩したコードコメントなど)。 * 新規に第三者が提供するソフトウェアの導入は、必要な場合以外は控えるのが望ましいと考えています。 ### ヘルパークラスと関数 スマート ポインタをより簡単におよび直観的に使用するために、ライブラリにはいくつかのヘルパークラスとヘルパー関数を準備しました。 | ヘルパー | 説明 | | --- | --- | |[REGION:tablesection]クラス[/REGION]|| | TSharedFromThis |「this」から TSharedRef を取得するために、独自のクラスを「this」から派生させることができます。 | |[REGION:tablesection]関数[/REGION]|| | MakeShareable() | C++ ポインタのシェアード ポインタを初期化するために使用します (暗黙的な変換を有効にします)。 | | StaticCastSharedRef() | 静的に型変換 (キャスト)するユーティリティ関数です。一般的に派生したタイプのダウンキャストに使用します。 | | ConstCastSharedRef() | const 参照を「可変」なスマート参照へ変換します。 | | DynamicCastSharedRef() | 動的に型変換 (キャスト) するユーティリティ関数です。一般的に派生したタイプのダウンキャストに使用します。 | | StaticCastSharedPtr() | 静的に型変換 (キャスト) するユーティリティ関数です。一般的に派生したタイプのダウンキャストに使用します。 | | ConstCastSharedPtr() | const スマート参照を「可変」なスマート参照へ変換します。 | | DynamicCastSharedPtr() | 動的に型変換 (キャスト) するユーティリティ関数です。一般的に派生したタイプのダウンキャストに使用します。 | ## スマート ポインタの実装に関する詳細 アンリアル スマート ポインタ ライブラリに実装されているさまざまなタイプのスマート ポインタは、パフォーマンス、メモリーの観点から一般的特徴を共有します。 ### パフォーマンス 共有のポインタの使用を考える際は、パフォーマンスの考慮が常に重要となります。シェアード ポインタの使用は、一般的にかなり高速なパフォーマンスをしますが、シェアード ポインタは場所を問わず使用するものではありません。特定のハイレベルなシステム、ツール、プログラミングなどに有益ですが、ローレベルなエンジンやレンダリング パスには適していません。 シェアード ポインタがパフォーマンスにもたらすいくつかの利点は以下となります。 * 全ての操作を定数時間で実施 * シェアード ポインタの間接参照が C++ ポインタ同様に迅速 * シェアード ポインタのコピーにメモリーを決して割り当てない * スレッドセーフ バージョンはロックフリー * Boost や STL と比較して高速実装です シェアード ポインタがパフォーマンスにもたらす欠点は以下となります。 * ポインタの作成やコピー作業のオーバーヘッド * 参照カウント式のハウスキーピング * C++ ポインタよりも多くのメモリを使用 * 参照コントローラに対し余分ヒープ割り当て * それぞれの固有オブジェクトがいくつものシェアード ポインタによって参照される点 * 弱い参照のアクセスはシェアード ポインタのアクセスよりも若干遅い点 ### メモリ使用量 全てのシェアードポインタ (TSharedPtr、TSharedRef、TWeakPtr) は 8 バイト (32 ビットでコンパイル時) で構成は以下の通りです。 * C++ ポインタ (uint32) * 参照コントローラ ポインタ (uint32) [REGION:note] 弱い参照を組み込んでいる TSharedFromThis も 8 バイトです。 [/REGION] 参照コントローラ オブジェクトは 12 バイト (32 ビットでコンパイル時) で構成は以下の通りです。 * C++ ポインタ (uint32) * 共有参照カウント (uint32) * 弱い参照カウント (uint32) [REGION:note] いくつものシェアード / 弱い参照がオブジェクトを参照しても、オブジェクトに対し 1 つの参照コントローラーのみが作成されます。 [/REGION] ### リフレクション機能 シェアード ポインタは非侵入型です。つまり、クラス自体はシェアード ポインタ (または参照) によって所有されているか否かが分かりません。しかし時には共有参照として現インスタンスにアクセスしたい場合があります。これを解決するには、TSharedFromThis<> からクラスを派生します。 TSharedFromThis<> からクラスを派生させることにより、`AsSharedRef()` メソッドを使用して this を共有参照へ変換することができます。常に共有参照を返すクラス ファクトリと一緒に使用すると便利です。 class FAnimation : public TSharedFromThis { void Register() { // this に対するシェアード参照へアクセス TSharedRef SharedThis = AsSharedRef(); // シェアード参照を期待するクラス機能 AnimationSystem::RegisterAnimation( SharedThis ); } } ### キャスト シェアード ポインタ (そして参照ポインタ) を簡単にキャストすることができます。アップキャストは C++ ポインタと同様、暗黙的に行います。 const を外すキャストをします (不正ですが時には必要です)。 ConstCastSharedPtr( ... ) 静的キャスト (しばし派生したクラス ポインタのダウンキャストに使用されます)。 StaticCastSharedPtr( ... ) [REGION:note] 動的キャストはサポートされていません (実行時型情報 (RTTI) ではありません)。代わりに上記の静的キャストを使用してください。 [/REGION] ### スレッド セーフティ 通常のシェアード ポインタは単一スレッドのみでのアクセスが安全です。マルチスレッド対応なアクセスとする場合、ポインタ クラスのスレッド セーフなバージョンを使用します。 * TThreadSafeSharedPtr * TThreadSafeSharedRef * TThreadSafeWeakPtr * TThreadSafeSharedFromThis<> これらのバージョンはアトミックな参照カウントが原因で若干遅いですが、動作は通常の C++ ポインタと一致する場合が多いです。 * Read と Copy は常にスレッド セーフです。 * Write と Reset は安全性のため同期しなくてはいけません。 [REGION:warning] ポインタが 1 つ以上のスレッドからアクセスされることが無いと知っている場合、スレッド セーフ バージョンは使用しないでください。 [/REGION] ## 使用詳細 「SharedPointerTesting.h」ファイル (格納場所は [UE4RootLocation]/Engine/Source/Runtime/Core/Public/Templates/) にシェアード ポインタとシェアード参照を使用した各種例が含まれています。 ### ヒント * C++ ポインタを新規のシェアード ポインタへ渡すときは、通常は operator new で領域を確保します。 * スマート ポインタを関数のパラメータとして渡す際に、TWeakPtr ではなく TSharedRef または TSharedPtr を使用します。 * 「スレッド セーフ」なスマート ポインタは多少動きが遅くなります。必要な時のみ使用してください。 * シェアード ポインタを前方宣言して期待通りの不完全な型にすることができます。 * 互換タイプのシェアード ポインタは暗黙的に変換されます (例えばアップキャスト)。 * プログラムをわかりやすく簡潔にするために TSharedRef< MyClass > へ typedef を作成することができます。 * 最高のパフォーマンスのために TWeakPtr::Pin の呼出しを最小限に抑えます (もしくは TSharedRef / TSharedPtr へ変換)。 * TSharedFromThis から派生させると、クラスは共有参照としてクラス自体を返すことができます。 * 派生したオブジェクト クラスにポインタをダウンキャストするには **StaticCastSharedPtr()** 関数を使用します。 * const オブジェクトはシェアード ポインタを完全にサポートします! * **ConstCastSharedPtr()** 関数を使用して const シェアード ポインタを可変にすることができます。 * 常に深いスタック フレームで C++ 参照へ変換します。シェアード ポインタはメンバ参照に最適ですが、一時的なスタック領域には向いていません。 * C++ ポインタとは異なり、シェアード ポインタは memcpy ができません。シェアード ポインタの配列の使用時はこの点を考慮してください。 ### 制限事項 * シェアード ポインタはアンリアル オブジェクト (UObject クラス) と互換性がありません! * 現時点では通常のデストラクタ― (カスタムデリータではありません) を持つたった 1 つのタイプです。 * 動的に割り当てられた配列はまだサポートされていません (例 `MakeSharable( new int32[20] )`)。 * TSharedPtr/TSharedRef の暗黙的な bool への変換はまだサポートされていません。 ### 他の実装との相違点 * タイプ名とメソッド名は、以前よりもアンリアルのコードベースと一致しています。 * 「スレッド セーフティ」な機能は強制ではなくオプション機能です。 * TSharedFromThis は共有のポインタではなく、共有の参照を返します。 * 省略されている機能もあります (例えば use_count()、unique()、など)。 * 例外は認めません (関連する全ての機能は省略されました)。 * カスタム アロケータとカスタムな delete 関数はまだサポートされていません。 * この実装は null 可能ではないスマート ポインタをサポートしています (TSharedRef)。 * MakeShareable や NULL の代入など、いくつかの新機能が追加されました。