11

Rule of Zeroというイディオムについて説明している記事があります。

ここに抜粋があります:

class module {
public:
    explicit module(std::wstring const& name)
    : handle { ::LoadLibrary(name.c_str()), &::FreeLibrary } {}

    // other module related functions go here

private:
    using module_handle = std::unique_ptr<void, decltype(&::FreeLibrary)>;

    module_handle handle;
};

RAII 機能を再利用unique_ptrするため、困難で冗長な Rule of Five ラッパーの実装を気にする必要はありません。

このように提示された(ハンドルベースのリソースをunique_ptrそのように管理する)と、私にとってはハックのように見えますが、解決しようとしているものの最善の解決策ではありません。あまりにも多くの仮定が暗黙のうちに仮定されています:

  • #define(またはtypedef)HANDLEが構築されている基本型をピアリングして使用することができます。私にとって、これは隠された知識であるべきであり、解決策はインターフェースが利用可能にするもののみに基づいている必要があります: HANDLE.
  • ハンドルは何でもかまいません。ポインター型である必要はありません。

私はこのイディオムを使いたいと思っていますが、私が遭遇する多くの状況では不十分です。

このハンドルに焦点を当てた RAII ラッパーは既に作成されており、クールなライブラリで使用できますか? 誰もがそのようなツールを使用していますが、私は気づいていませんか? (1つだけでなく、さまざまな種類の所有権のためにそのようなツールがあると便利だと思います)

編集1

これは、プラットフォーム固有のリソース ハンドルに関するものではありません。たとえば、glGenListsある種のハンドルを返します。これは であり、これGLuintを呼び出す必要がありますglDeleteLists。既に述べたように、リソース ハンドルはポインター型である必要はなく、この仮定は仮定されるべきではありません。

編集2

前者のサンプルのルール オブ ゼロは、既存のツールを使用することにより、unique_ptrハンドル管理の優れたショートカットとして表示されます。それが必要とする余分な仮定により、それは不十分になります。正しい仮定は、ハンドルがあり、ハンドルによって指定されたリソースを破棄するリソース破棄関数があることです。ハンドルが a 、 a 、何であれ、それは問題ではなく、さらに悪いことに、内部型をピアリングする必要さえありません。ハンドルを RAII のように管理する目的で、イディオムがそれで良いと言っていて、そのような状況では適用できない場合、それは良くないと感じます。void *GLuintHANDLE

編集3

たとえば、新しいサード パーティの C ライブラリの使用を担当しているとします。と が含まれていFooHandle create_foo()ますvoid destroy_foo(FooHandle)。「ゼロの法則を使って FooHandle を使おう」と。わかりました、あなたはあなたが自問するものにa unique_ptr、 aを使って行きますか? toの公開されていない基本型を使用するにunique_ptrFooHandle、ポインターですか? それは? なので、そのまま使用できますか、それとも @NicolBolas が彼の回答で行ったようなことを (再) する方がよいでしょうか。私にとっては、このような些細な状況であっても、リソース ハンドルを管理するのに理想的ではないことがすでに明らかになっているように思えます。unique_ptrFooHandleinttypedefunique_ptr

免責事項:

私は自分自身を再定式化し、よりよく表現しようとしました:

編集4

私は探していたものを見つけました。再定式化された質問の答えとしてそれを入れました: https://stackoverflow.com/a/14902921

4

2 に答える 2

12

背景: ゼロのルール

何よりもまず、その記事は、言及されているリソース ハンドル ラッパーの例よりも、はるかに一般的な考え方についてのものです。

ポイントは、クラスに「重要な」所有権セマンティクスを持つメンバーが含まれている場合は常に、クラスは、含まれているクラスの適切な値セマンティクス (コピー、割り当て、移動、破棄) を保証するメカニズムに責任を負うべきではないということです。むしろ、各構成要素自体が適切に「Rule-Of-3+」を実装して、複合クラスがコンパイラのデフォルトの特別なメンバーを利用できるようにする必要があります。

さらに、新しい標準のスマート ポインター型は、含まれている型に対してこれを実現するタスクを大幅に簡素化します。ほとんどの場合、注意が必要なメンバーはポインターです。std::unique_ptr、shared_ptr、weak_ptr は、ここでのニーズに対処します。

カスタム リソース タイプの場合、Rule-Of-3+ ラッパー クラスを記述する必要がある場合がありますが、1 回だけです。そして、残りのクラスはゼロのルールの恩恵を受けます。


弾丸:

  • #define (または typedef) HANDLE が構築されている基本型をピアリングして使用することができます。私にとって、これは隠された知識であるべきであり、解決策は、インターフェースが利用可能にするもの、HANDLE のみに基づいている必要があります。

A: 90% のstd::unique_ptr<>確率で、カスタム デリーターを使用できます (使用する必要があります)。クリーンアップに関する知識と責任は、引き続きアロケーターにあります。あるべき姿。

残りのケースでは、他の方法ではサポートされていない、特定のリソース用の単一のラッパー クラスを作成する必要があります。

  • ハンドルは何でもかまいません。ポインター型である必要はありません。

彼らはできます。たとえば、boost::optional を参照してください。または、そのラッパーを記述します。ポイントは、リソースに対して分離する必要があるということです。そのようなリソースをたまたま所有/含んでいるクラスを複雑にしたくありません。

于 2013-02-14T00:58:05.967 に答える
10

このハンドルに焦点を当てた RAII ラッパーは既に作成されており、クールなライブラリで使用できますか?

あなたの場合、それは呼ばれunique_ptrます。観察:

struct WndLibDeleter
{
  typedef HANDLE pointer;

  void operator()(HANDLE h) {::FreeLibrary(h);}
};

using WndLibrary = std::unique_ptr<HANDLE, WndLibDeleter>;

WndLibrary LoadWndLibrary(std::wstring const& name)
{
  return WndLibrary(::LoadLibrary(name.c_str()));
}

デリーターを使用unique_ptrすると、NullablePointer である任意の種類のオブジェクトを保存およびサービスできます。HANDLEは NullablePointer であるため、サービスを提供できます。

NullablePointers ではないオブジェクトの場合は、別のものを使用する必要があります。ゼロの法則のポイントは、「何か他のもの」をできるだけ小さくすることです。それは型の周りの RAII ラッパーにすぎず、型へのアクセスと移動サポートを提供するだけです。したがって、コードの大部分は、明示的なコピー/移動コンストラクターを必要としません。それらのいくつかのリーフクラスだけです。

于 2013-02-14T01:11:13.700 に答える