Language:
Page Info
Tags:
Engine Version:

アンリアル スマート ポインタ ライブラリ

スマートポインタ

アンリアル スマート ポインタ ライブラリ は、共有の参照 (TSharedRef)、シェアード ポインタ (TSharedPtr)、弱いポインタ (TWeakPtr) だけでなく、関連するヘルパー関数とクラスをカスタム実装したものです。この実装は C++0x 標準ライブラリの shared_ptr と Boost スマート ポインタを手本に実装しました。

タイプ

説明

シェアード ポインタ (TSharedPtr)

参照カウント式、非侵入型の信頼できるスマート ポインタ。

共有の参照 (TSharedRef)

non-nullable 型、参照カウント式の非侵入型の信頼できるスマート ポインタ。

弱いポインタ (TWeakPtr)

参照カウント式、非侵入型の弱いポインタの参照。

参照権の共有とシェアード ポインタの利点

利点

説明

クリーンなシンタックス

通常の C++ ポインタと同じ要領でシェアード ポインタをコピー、ポインターの参照先の値の取得、および比較することができます。

メモリーリークの防止

これ以上共有の参照が無い場合、リソースは自動的に破棄されます。

弱い参照

オブジェクトが破棄された時、弱いポインタで安全なチェックが可能になります。

スレッド セーフティ

複数のスレッドから安全なアクセスを可能にする「スレッドセーフ」なバージョンを実装しています。

ユビキタス

事実上どのようなタイプのオブジェクトに対してもシェアード ポインタを作成できます。

ランタイムの安全性

共有の参照は決して null にはならず、常にポインタから参照先の値を取得します。

参照の循環なし

参照の循環を中断するために弱いポインタを使用します。

意図がわかる

オブザーバーから簡単にオブジェクトのオーナーがわかります。

パフォーマンス

シェアード ポインタはオーバーヘッドを最小限に抑えます。全ての操作は一定時間で行われます。

堅牢な機能

不完全な型、型キャストなどに対して const や前方宣言をサポートします。

メモリ

64 ビット環境の C++ ポインタサイズの 2 倍程度です (加えて共有の 16 倍と参照コントローラー)。

カスタム ライブラリを作成する理由

  • std::shared_ptr (および tr1::shared_ptr) はまだ全てのプラットフォームに対応していません。

  • 全てのコンパイラーやプラットフォームに対して一段と一貫性のある実装を有効にします。

  • 他のアンリアル コンテナや型とシームレスに機能します。

  • スレッディングや最適化を含めてプラットフォームの特性を上手にコントロールします。

  • スレッドセーフをオプション機能にします (パフォーマンスのため)。

  • 独自の改善を追加しました (MakeShareable、NULL 値の割り当てなど)。

  • この実装に例外は必要ありません。

  • パフォーマンスのさらなるコントロールを強化しました (インライン化、メモリ、バーチャルメモリの使用など)。

  • 潜在的により簡単なデバッグ作業 (十分なコードコメントなど)。

  • 必要がない限り新規に第三者が提供するソフトウェアの導入は控えるのが望ましいと考えています。

ヘルパークラスと関数

スマート ポインタを簡単かつ直観的に使用するために、ライブラリにはいくつかのヘルパークラスとヘルパー関数を準備しました。

ヘルパー

説明

クラス

TSharedFromThis

「this」から TSharedRef を取得するために、独自のクラスを「this」から派生させることができます。

関数

MakeShareable()

C++ ポインタのシェアード ポインタを初期化するために使用します (暗黙的な変換を有効にします)。

StaticCastSharedRef()

静的に型変換 (キャスト)するユーティリティ関数です。一般的に派生した型のダウンキャストに使用します。

ConstCastSharedRef()

const 参照を「可変」 (mutable) なスマート参照へ変換します。

DynamicCastSharedRef()

動的に型変換 (キャスト) するユーティリティ関数です。一般的に派生した型のダウンキャストに使用します。

StaticCastSharedPtr()

静的に型変換 (キャスト) するユーティリティ関数です。一般的に派生した型のダウンキャストに使用します。

ConstCastSharedPtr()

const スマート参照を「可変」 (mutable) なスマート ポインタに変換します。

DynamicCastSharedPtr()

動的に型変換 (キャスト) するユーティリティ関数です。一般的に派生した型のダウンキャストに使用します。

スマートポインタの実装に関する詳細

アンリアル スマート ポインタ ライブラリに実装されているさまざまなタイプのスマート ポインタは、パフォーマンス、メモリーに関して一般的特徴を共有します。

パフォーマンス

シェアード ポインタの使用を考える際は、パフォーマンスの考慮が常に重要となります。シェアード ポインタは一般的に高速ですが、場所を問わず使用するものではありません。特定のハイレベルなシステム、ツールのプログラミングなどに有益ですが、ローレベルなエンジンやレンダリング パスには適していません。

シェアード ポインタのパフォーマンス上の利点は以下のとおりです。

  • 全ての操作を一定時間で実施

  • シェアード ポインタの間接参照が C++ ポインタ同様に迅速

  • シェアード ポインタのコピーにメモリーを決して割り当てない

  • スレッドセーフ バージョンはロックフリー

  • Boost や STL と比較して高速実装

シェアード ポインタがパフォーマンスにもたらす欠点は以下のとおりです。

  • ポインタの作成やコピー作業のオーバーヘッド

  • 参照カウントのハウスキーピング

  • C++ ポインタよりも多くのメモリを使用

  • 参照コントローラに対し余分にヒープ割り当て

  • 各固有オブジェクトが任意の数のシェアード ポインタによって参照される点

  • 弱い参照のアクセスはシェアード ポインタのアクセスよりも若干遅い点

メモリ使用量

全てのシェアードポインタ (TSharedPtr、TSharedRef、TWeakPtr) は 8 バイト (32 ビットでコンパイル時) で構成は以下の通りです。

  • C++ ポインタ (uint32)

  • 参照コントローラ ポインタ (uint32)

弱いポインタを組み込んでいるため TSharedFromThis も 8 バイトです。

参照コントローラ オブジェクトは 12 バイト (32 ビットでコンパイル時) で構成は以下の通りです。

  • C++ ポインタ (uint32)

  • 共有参照カウント (uint32)

  • 弱い参照カウント (uint32)

いくつものシェアード ポインタ / 弱いポインタがオブジェクトを参照しても、オブジェクトに対し 1 つの参照コントローラーのみが作成されます。

リフレクション機能

シェアード ポインタは非侵入型です。つまり、クラス自体はシェアード ポインタ (または参照) によって所有されているかどうかは分かりません。一般的にこれは問題ありませんが、時には共有参照として現インスタンスにアクセスしたい場合があります。これを解決するには、TSharedFromThis<> からクラスを派生します。

TSharedFromThis<> からクラスを派生させることにより、AsSharedRef() メソッドを使用して this を共有参照へ変換することができます。常に共有参照を返すクラス ファクトリと一緒に使用すると便利です。

class FAnimation : public TSharedFromThis<FMyClass>
{
    void Register()
    {
        // this に対する共有参照にアクセス
        TSharedRef<FMyClass> SharedThis = AsSharedRef();

        // 共有参照を求める関数を指定
        AnimationSystem::RegisterAnimation( SharedThis );
    }
}

キャスト

シェアード ポインタ (および参照ポインタ) を簡単にキャストすることができます。アップキャストは C++ ポインタと同様、暗黙的に行います。

const を外すキャストをします (不正ですが時には必要です)。

ConstCastSharedPtr<T>( ... )

静的キャスト (しばしば派生したクラス ポインタのダウンキャストに使用されます)。

StaticCastSharedPtr<T>( ... )

動的キャストはサポートされていません (実行時型情報 (RTTI) ではありません)。代わりに上記の静的キャストを使用してください。

スレッド セーフティ

通常のシェアード ポインタは単一スレッドのみでのアクセスが安全です。マルチスレッド対応のアクセスとする場合、ポインタ クラスのスレッド セーフなバージョンを使用します。

  • TThreadSafeSharedPtr<T>

  • TThreadSafeSharedRef<T>

  • TThreadSafeWeakPtr<T>

  • TThreadSafeSharedFromThis<>

これらのバージョンはアトミックな参照カウントが原因で若干遅いですが、動作は通常の C++ ポインタと一致する場合が多いです。

  • Read と Copy は常にスレッド セーフです。

  • Write と Reset は安全性のため同期しなくてはいけません。

ポインタが 1 つ以上のスレッドからアクセスされることが無いとわかっている場合、スレッド セーフ バージョンは使用しないでください。

使用の詳細

「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 クラス) と互換性がありません!

  • 動的に割り当てられた配列はまだサポートされていません (例 MakeSharable( new int32[20] ))。

  • TSharedPtr/TSharedRef の暗黙的な bool への変換はまだサポートされていません。

他の実装との相違点

  • 型名とメソッド名は、アンリアルのコードベースと一致しています。

  • 「スレッド セーフティ」な機能は強制ではなくオプション機能です。

  • TSharedFromThis は共有のポインタではなく、共有の参照を返します。

  • 省略されている機能もあります (例えば use_count()、unique()、など)。

  • 例外は認めません (関連する全ての機能は省略されました)。

  • この実装はnon-nullable 型のスマート ポインタをサポートしています (TSharedRef)。

  • MakeShareable や NULL の代入など、いくつかの新機能が追加されました。