12

最近、 SOでRAIIに関する一般的な質問を投稿しました。ただし、HANDLEの例にはまだいくつかの実装上の問題があります。

AHANDLEはでtypedeffedさvoid *windows.hます。したがって、正しいshared_ptr定義は次のようになります。

std::tr1::shared_ptr<void> myHandle (INVALID_HANDLE_VALUE, CloseHandle);

例1 CreateToolhelp32Snapshot:戻りHANDLE、動作します。

const std::tr1::shared_ptr<void> h
    (CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL), CloseHandle);

定義で使用voidしているように(正しい方法は何ですか?)、このポインターを使用してさらにいくつかのwinapiコマンドを呼び出そうとすると、問題が発生します。それらは機能的には機能しますが、醜いので、より良い解決策が必要だと確信しています。

次の例でhは、は上部の定義を介して作成されたポインタです。

例2 OpenProcessToken:最後の引数はPHANDLE。キャストで中程度の醜い。

OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
    (PHANDLE)&h);

例3 Process32First:最初の引数はHANDLE。本当に醜い。

Process32First(*((PHANDLE)&h), &pEntry);

例4定数との簡単な比較HANDLE。本当に醜い。

if (*((PHANDLE)&h) == INVALID_HANDLE) { /* do something */ }

HANDLEに適切なshared_ptrを作成する正しい方法は何ですか?

4

4 に答える 4

11

例 1 で問題ありません

例 2 は間違っています。盲目的に PHANDLE にキャストすることで、shared_ptr ロジックがバイパスされます。代わりに、次のようにする必要があります。

HANDLE h;
OpenProcessToken(...., &h);
shared_ptr<void> safe_h(h, &::CloseHandle);

または、既存の shared_ptr に割り当てるには:

shared_ptr<void> safe_h = ....
{
  HANDLE h;
  OpenProcessToken(...., &h);
  safe_h.reset(h, &::CloseHandle);
}//For extra safety, limit visibility of the naked handle

または、PHANDLE を受け取る代わりに共有ハンドルを返す、独自の安全なバージョンの OpenProcessToken を作成します。

// Using SharedHandle defined at the end of this post
SharedHandle OpenProcess(....)
{
    HANDLE h = INVALID_HANDLE_VALUE;
    ::OpenProcessToken(...., &h);
    return SharedHandle(h);
}

例 3: これらの迂回路を使用する必要はありません。これは問題ないはずです:

Process32First(h.get(), ...);

例 4: 繰り返しますが、迂回路はありません:

if (h.get() == INVALID_HANDLE){...}

より良いものにするために、次のように typedef することができます。

typedef shared_ptr<void> SharedHandle;

さらに良いことに、すべてのハンドルを CloseHandle() で閉じる場合は、shared_ptr をラップし、適切なデリータを自動的に提供する SharedHandle クラスを作成します。

// Warning: Not tested. For illustration purposes only
class SharedHandle
{
public:
  explicit SharedHandle(HANDLE h) : m_Handle(h, &::CloseHandle){};
  HANDLE get()const{return m_Handle.get();}

  //Expose other shared_ptr-like methods as needed
  //...

private:
  shared_ptr<void> m_Handle;
};
于 2009-10-13T20:02:42.927 に答える
3

そのために shared_ptr を気にせずに、ATL::CHandle を使用してください。

理由は次のとおりです。

  • CHandleハンドルの RAII ラッパーであることがわかります。
  • shared_ptr<void>たら何だかわからない。
  • CHandle所有権を共有しません (ただし、共有所有権が必要な場合もあります)。
  • CHandleWindows 開発スタックの標準です。
  • CHandleカスタムデリータよりもコンパクトですshared_ptr<void>(入力/読み取りが少なくなります)。
于 2012-02-02T11:00:54.383 に答える
2

ブースト 2を見てみましょう: shared_ptr はリソース ハンドルをラップします

于 2009-10-13T20:21:07.990 に答える
2

これは私の代替手段です。これは、常に後で逆参照する必要.get()があり、ファンクターまたはラムダが必要であることを除いて、非常に優れています。

template<typename HandleType, typename Deleter>
std::shared_ptr<HandleType> make_shared_handle(HandleType _handle, Deleter _dx)
{
    return std::shared_ptr<HandleType>(new HandleType(_handle), _dx);
}

それから:

auto closeHandleDeleter = [](HANDLE* h) {
    ::CloseHandle(*h);
    delete h;
};
std::shared_ptr<HANDLE> sp = make_shared_handle(a_HANDLE, closeHandleDeleter);
f_that_takes_handle(*sp.get());

これについて私が最も気に入っているのは、これにアクセスするための余分な作業がないことです。

std::weak_ptr<HANDLE> wp = sp; // Yes. This could make sense in some designs.

もちろん、ヘルパー関数はどのようなハンドル タイプでも機能します。

于 2012-02-02T01:41:26.460 に答える