std::unique_ptr
には2つのテンプレートパラメータがあり、2番目のテンプレートパラメータは使用するデリータです。この事実のおかげで、1は、簡単にカスタム削除手段を必要とするタイプ、(例えばSDL_Texture
を)にunique_ptr
は、次のようにすることができますエイリアス:SDL2PtrDeleter
はファンクタをあるstd :: shared_ptrを作成またはリセットするたびにdeleterを指定する必要性を避けるにはどうすればよいですか?
using SDL_TexturePtr = unique_ptr<SDL_Texture, SDL2PtrDeleter>;
...デリータとして使用されます。
は、このエイリアスを考えると、プログラマは、思いやりのある、あるいはカスタム削除手段については知らなくてもSDL_TexturePtr
を構築し、リセットすることができます:
SDL_TexturePtr ptexture(SDL_CreateTexture(/*args*/));
//...
ptexture.reset(SDL_CreateTexture(/*args*/));
std::shared_ptr
、一方で、テンプレートパラメータを持っていません、型の一部としてdeleterを指定することができるので、以下は違法です。
// error: wrong number of template arguments (2, should be 1)
using SDL_TextureSharedPtr = shared_ptr<SDL_Texture, SDL2PtrDeleter>;
したがって、最も適切なのは型
using SDL_TextureSharedPtr = shared_ptr<SDL_Texture>;
しかし、これは、ユーザが知っている必要がありますので、使用し、それに彼らが構築するたびに指定するか、とにかくSDL_TextureSharedPtr
をリセットするには、明示的デリータ機能をshared_ptr<SDL_Texture>
を使用しての上にいくつかの利点があります:エイリアスです
SDL_TextureSharedPtr ptexture(SDL_CreateTexture(/*args*/), SDL_DestroyTexture);
//...
ptexture.reset(SDL_CreateTexture(/*args*/), SDL_DestroyTexture);
上の例からわかるように、ユーザーはSDL_Texture
(つまりSDL_DestroyTexture()
)を削除し、そのたびにポインタを渡す正しい関数を知る必要があります。これは不便であるばかりでなく、プログラマが間違った関数をdeleterとして指定することによってバグを導入する可能性が小さくなります。
私はどういうわけか、共有ポインタそのものの型でデリターをカプセル化したいと思っています。 shared_ptr
のインタフェースを複製するだろうstd::shared_ptr<T>
を、ラップ、クラスを作成します
:方法がないので、私の知る限り、単にタイプの別名を使用することによって、これを達成するために、私は3つのオプションを検討してきました独自のテンプレートパラメータを介してDeleter Functorを指定することができます。このラッパーは、コンストラクターまたは
reset()
メソッドを基にしたstd::shared_ptr<T>
のメソッドを独自のコンストラクターまたはreset()
メソッドから呼び出すときに、そのdeleterインスタンスのoperator()
へのポインターをそれぞれ提供します。欠点は、もちろん、かなり大きい、std::shared_ptr
のインターフェイスは、このラッピングクラスWETで複製する必要があるということです。std::shared_ptr<T>
のサブクラスを作成します。これにより、独自のテンプレートパラメータを使用してDeleter Functorを指定できます。これは、public
の継承を仮定すると、shared_ptr
のインタフェースを複製する必要性を避けるのに役立ちますが、独自のワームの缶を開くことになります。std::shared_ptr
はfinal
ではありませんが、非仮想的なデストラクタを持っているので、サブクラス化するようには設計されていないようです(この例では問題はありません)。さらに悪いことに、reset()
メソッドはshared_ptr
で仮想メソッドではないため、無効にすることはできません。不正使用の扉を開きます。public
継承では、ユーザはサブクラスのインスタンスへの参照をいくつかのAPIに渡し、std::shared_ptr<T>&
の実装でreset()
が呼び出され、私たちのメソッドを完全に回避します。非公開継承では、オプション#1と同じようになります。上記のオプションの両方について
、最終的に、SDL_TextureSharedPtr
はMySharedPtr<T, Deleter>
が私たちの(サブ)クラスであると仮定すると、以下のように表すことができる:
using SDL_TextureSharedPtr = MySharedPtr<SDL_Texture, SDL2PtrDeleter>;
- 3番目のオプションはここにあって、それには専門の
std::default_delete<T>
が含まれていました。これは、std::shared_ptr<T>
がstd::default_delete<T>
を使用しているという誤った仮定に基づいています。具体的には、除外者が明示的に指定されていない場合はunique_ptr
のようになります。これはではなく、の場合です。これを指摘するために@DieterLücking
に感謝します!
は、これらのオプションと上記の理由を考えると、ここで私の質問です。
インスタンスが作成されるたびにstd::shared_ptr<T>
のデリータを指定しなくても簡単な方法がありませんでしたか?reset()
?
もしそうでない場合は、リストに載っているオプションが正しいと思いますか?これらのオプションの1つを別のものよりも優先させる他の客観的な理由はありますか?
クラス自体をラップしてRAIIに準拠させ、 'shared_ptr'の最初のテンプレート引数を調整するのはなぜですか? – Columbo
私はあなたが本当にカスタムディレクターを必要とする理由はまだ見ていません。 'SDL_DestroyTexture(ptr);'をデストラクタに置かないのはデフォルトのデリゲータと同じですか? – lorro
@lorro:それはテクスチャのためだけに機能します。目標はどんなタイプのTでも動作することです。 – Cornstalks