6

いくつかのハンドルがあり、それを閉じる必要があります。コードには、ハンドルが閉じられている可能性のある場所がいくつかあります。では、これはハンドルを閉じる正しい方法ですか?

HANDLE h;
....
if ( h != INVALID_HANDLE_VALUE ) {
  ::CloseHandle(h);
  h = INVALID_HANDLE_VALUE;
}

ビットマップ ハンドルについて同じ質問があります。

HBITMAP hb;
....
if ( hb != INVALID_HANDLE_VALUE ) {
  ::DeleteObject(hb);
  hb = INVALID_HANDLE_VALUE;
}

編集:誤解があると思います。ハンドルCloseHandleを閉じるためのものです。ハンドルを閉じる正しい方法を知りたいです。ポインターの削除でも同様の状況が発生します。

Foo *foo = new Foo();

// for example there is 2 functions that can delete foo
void bar() {
  ....
  delete foo;
}
void duck() {
  ....
  delete foo;
}

したがって、次のコードは問題を意味します。

bar();
duck();

この場合、いくつかの回避策があります。bar&duck関数を次のように定義する必要があります。

void bar() {
  ....
  if (foo) {
    delete foo;
    foo = NULL;
  }
}
void duck() {
  ....
  if (foo) {
    delete foo;
    foo = NULL;
  }
}

したがって、foo を繰り返し削除することは避けます。問題は、ハンドルを閉じる適切な方法は何ですか? つまり、繰り返しハンドルを閉じる問題を回避するにはどうすればよいですか?

4

5 に答える 5

13

HANDLEを使用するすべての関数が を使用するわけではなくCloseHandle()、代わりに他の終了関数を使用する関数もあります。また、すべてのHANDLE値が を使用するわけではありませんINVALID_HANDLE_VALUENULL代わりに使用するものもあります。

HBITMAPを使用することはなくINVALID_HANDLE_VALUE、常に を使用しNULLます。DeleteObject()そして、あなたが所有していないものを呼び出すべきではHBITMAPありません。

簡単に言えば、汎用ハンドル管理を作成しようとしている場合は、気にしないでください。あなたはそれを間違える可能性があります。ハンドルを割り当てたり開いたりする場合は、それを閉じる正しい方法を知っている必要があります。推測することはできません。

ハンドルを自分で管理したい場合は、RAII が最適です。特殊な特性を持つテンプレート化されたクラスを使用して、さまざまなタイプのハンドルのコードの重複を減らすことを好みます。たとえば、次のようになります。

template< class traits >
class HandleWrapper
{
private:
    traits::HandleType FHandle;

public:
    HandleWrapper()
        FHandle(traits::InvalidValue)
    {
    }

    HandleWrapper(const traits::HandleType value)
        FHandle(value)
    {
    }

    ~HandleWrapper()
    {
        Close();
    }

    void Close()
    {
        if (FHandle != traits::InvalidValue)
        {
            traits::Close(FHandle);
            FHandle = traits::InvalidValue;
        }
    }

    bool operator !() const {
        return (FHandle == traits:::InvalidValue);
    }

    operator bool() const {
        return (FHandle != traits:::InvalidValue);
    }

    operator traits::HandleType() {
        return FHandle;
    }
};

.

struct KernelHandleTraits
{
    typedef HANDLE HandleType;
    static const HANDLE InvalidValue = INVALID_HANDLE_VALUE;

    static void Close(HANDLE value)
    {
        CloseHandle(value);
    }
};

HandleWrapper<KernelHandleTraits> hFile(CreateFile(...));

.

struct NullKernelHandleTraits
{
    typedef HANDLE HandleType;
    static const HANDLE InvalidValue = NULL;

    static void Close(HANDLE value)
    {
        CloseHandle(value);
    }
};

HandleWrapper<NullKernelHandleTraits> hMapping(CreateFileMapping(...));

.

struct FileMapViewTraits
{    
    typedef void* HandleType;
    static const void* InvalidValue = NULL;

    static void Close(void *value)
    {
        UnmapViewOfFile(value);
    }
};

HandleWrapper<FileMapViewTraits> hView(MapViewOfFile(...));

.

struct GDIBitmapHandleTraits
{    
    typedef HBITMAP HandleType;
    static const HBITMAP InvalidValue = NULL;

    static void Close(HBITMAP value)
    {
        DeleteObject(value);
    }
};

HandleWrapper<GDIBitmapTraits> hBmp(CreateBitmap(...));

等。

于 2012-10-25T00:29:46.947 に答える
3

RAIIパターンを使用します。

コンストラクターでハンドルを割り当て、デストラクタでハンドルを破棄するクラスにハンドルをラップします。MFCにはいくつかの例があります。たとえば、のようなGDIオブジェクトのCGdiObjectクラスHBITMAPです。

このSOの質問も参照してください:C++のRAIIとスマートポインター

于 2012-10-23T11:36:44.500 に答える
2

はい。

  • CloseHandle()Windowsカーネル オブジェクトハンドルを閉じます。
  • DeleteObject()GDI オブジェクトを削除します。

あなたの混乱は、どちらも「ハンドル」と呼ばれていることに起因すると思いますが、オブジェクトの異なる「クラス」です。ハンドルという用語は、HBITMAPここでは「不透明な識別子」として使用されています。"ハンドル" == "Windows カーネル ハンドル" を想定しているドキュメントもたくさんあります。

一般に、何かを削除する方法を知りたい場合は、コンストラクターのドキュメントを参照してください。

于 2012-10-23T11:04:26.830 に答える
0

RAII ではありませんが、ハンドラーを削除/閉じるのに役立ちます。

class HandleDel : boost::notcopyable{
public:
    HandleDel(HANDLE h, HANDLE invalid, BOOL(WINAPI *del)(HANDLE)):
        h(h), invalid(invalid), del(del){
    }
    ~HandleDel(){
        if ( h != invalid ) del(h);
    }
private:
    HANDLE h;
    HANDLE invalid;
    BOOL(WINAPI *del)(HANDLE);
};
于 2015-10-30T07:32:07.303 に答える