私は、自動化されたリソース管理のためのC ++でどのようなラッパークラスを使用する必要がありますか?
-
19-09-2019 - |
質問
私は++アマチュアCです。私はいくつかのWin32 APIのコードを書いていると豊富ハンドルと不気味複合的に割り当てられたオブジェクトがあります。だから私は思っていた - リソースの管理が容易になるだろういくつかのラッパークラスがあります。
? 私はいくつかのデータをロードする際に、たとえば、私はCreateFile()
でファイルを開き、HANDLE
を取得します。私はそれで終わりだとき、私はそれにCloseHandle()
呼び出す必要があります。しかし、任意の合理的に複雑なロード機能のための例外を言及しないように、可能な出口点の数十があるでしょう。
私は実行がスコープを残した後、自動的にCloseHandle()
を呼ぶようなラッパークラスのいくつかの種類でハンドルを包むことができれば、それは素晴らしいことだので。さらに良いこと - 。それは私がして、その他の機能のうち、それを周りに渡すことができます数えるいくつかの参照を行うことができ、そしてそれは、リソースを解放する場合にのみ、最後の参照左スコープ
コンセプトは単純です - しかし、標準ライブラリのようなものはありますか?私は道で、Visual Studioの2008を使用している、と私はブーストか何かのようなサードパーティ製のフレームワークを添付する必要はありません。
解決
あなた自身を書きます。これは、わずか数行のコードです。それは、の価値があるの一般的な再利用可能なバージョンを提供しないということだけで、このような単純な作業です。
struct FileWrapper {
FileWrapper(...) : h(CreateFile(...)) {}
~FileWrapper() { CloseHandle(h); }
private:
HANDLE h;
};
ジェネリックバージョンがしなければならないだろうかを考えてみて:あなたは機能ののいずれかののペアを指定することができますので、それをパラメータ化する必要があるだろう、とのいずれかの引数の数にそれら。ただ、このようなオブジェクトをインスタンス化することはおそらく上記のクラス定義とコードの多くのラインを取るます。
もちろん、C ++ 0xのは、ラムダ式を追加して、ややバランスを崩す可能性があります。 C ++ 0xのが来るサポートされていたら、私たちはのように、2つのラムダ式は簡単に、汎用的なラッパークラスに渡すことができます。のかもしれない、そのような一般的なRAIIクラスを参照してくださいブーストか何かに追加されました。
しかし、現時点では、それはあなたがそれを必要なときだけあなた自身をロールバックする方が簡単です。
参照カウントを追加するために、私はそれに対して助言するだろう。参照カウントは、(突然ハンドルが動的に割り当てられる必要があり、参照カウンタは、すべての割り当てに維持する必要が)高価であり、非常に難しいが権利を取得します。それはちょうどスレッド環境での微妙な競合条件にあふれた領域です。
は、をした場合行うのちょうどboost::shared_ptr<FileWrapper>
ような何かを、参照カウントが必要になります。shared_ptr
にカスタムアドホックRAIIクラスをラップ
他のヒント
基本的に、fstream
は、ファイルハンドルのための良いC ++ラッパーです。オブジェクト指向の方法で十分にテスト、ポータブル、および拡張可能であることを意味し、標準の一部です。ファイルリソースの場合、それは素晴らしい概念です。
しかし、fstream
はすなわち、スレッド、プロセス、同期オブジェクト、メモリマップドファイルなど、ファイルのためではなく、一般的なハンドルのために働くます。
これらのラッパーは、ATLと呼ばれています。
あなたのハンドルがイベントまたは類似している場合、、= <のhref =「https://docs.microsoft.com/en-us/cpp/atl/reference/chandle-class?view=vs-2019」のrelを使用"nofollowをnoreferrer"> CHandle のクラスます。
あなたのハンドルがファイルである場合は、CAtlFileを使用することがCreateFileのとのReadFileなどのAPIをラップし、1を得ます。
他の有用なラッパーはATLに存在している、CAtlFileMapping<T>
がメモリオーバーRAIIラッパーがファイルをマッピングされている、CPath
ので上のパス処理のためShell32はAPIをラップし、かつます。
ATLは、大規模なライブラリですが、ファイル、文字列やコレクションなどの低レベルのものは単離されています。あなたはすべてのWin32アプリケーションでそれらを使用することができます。ヘッダのみ、あなたは何とリンク、またはMFCまたはCRTのような余分なDLLを配布する必要はありませんされ、コードはWinAPIの呼び出しにコンパイルし、だけで動作します。
彼らはVS2003または2005でMFCから分割された、すなわちのVisual Studio 2008は間違いなくそれらを持って、覚えていません。 1つの警告は、あなたがVSのフリーウェアバージョンを使用している場合、それは2015年以降でなければなりません、しかしあります。
ここで「C / C ++を経由してWindowsのからEnsureCleanupコードをオフに基づいて、1です: http://www.codeproject.com/KB/cpp/template2003.aspx
MFCは、いくつかの適切なプリミティブは、(のCFile <見ています/>など)ではなく、標準ライブラリます。
のVisual C ++ 2008 Feature Packを経由TR1をサポートし、TR1はshared_ptrのを含んでいます。私はこれを使用する - それは非常に強力なスマートポインタクラスだと、あなたが求めている資源管理の種類を行うために一般化することができる。
。TR1を効果的に標準の拡張です。私はそれはまだ正式に「プレ標準」だと信じていますが、効果的にあなたはそれがロックダウン検討することができます。
(これらのハンドル、ポインタをハンドルに期待していないため)使用することができ、私は標準ライブラリに何かがあるとは思わない、と私も疑問(ブーストのように)ポインタを共有している。
スコープガード以下、1を自分で書くのは難しいことではありませんのイディオム(あなたがそう選択した場合、テンプレート/関数ポインタなどを利用して)ます。
template <typename Traits>
class unique_handle
{
using pointer = typename Traits::pointer;
pointer m_value;
auto close() throw() -> void
{
if (*this)
{
Traits::close(m_value);
}
}
public:
unique_handle(unique_handle const &) = delete;
auto operator=(unique_handle const &)->unique_handle & = delete;
explicit unique_handle(pointer value = Traits::invalid()) throw() :
m_value{ value }
{
}
unique_handle(unique_handle && other) throw() :
m_value{ other.release() }
{
}
auto operator=(unique_handle && other) throw() -> unique_handle &
{
if (this != &other)
{
reset(other.release());
}
return *this;
}
~unique_handle() throw()
{
close();
}
explicit operator bool() const throw()
{
return m_value != Traits::invalid();
}
auto get() const throw() -> pointer
{
return m_value;
}
auto get_address_of() throw() -> pointer *
{
ASSERT(!*this);
return &m_value;
}
auto release() throw() -> pointer
{
auto value = m_value;
m_value = Traits::invalid();
return value;
}
auto reset(pointer value = Traits::invalid()) throw() -> bool
{
if (m_value != value)
{
close();
m_value = value;
}
return static_cast<bool>(*this);
}
auto swap(unique_handle<Traits> & other) throw() -> void
{
std::swap(m_value, other.m_value);
}
};
template <typename Traits>
auto swap(unique_handle<Traits> & left,
unique_handle<Traits> & right) throw() -> void
{
left.swap(right);
}
template <typename Traits>
auto operator==(unique_handle<Traits> const & left,
unique_handle<Traits> const & right) throw() -> bool
{
return left.get() == right.get();
}
template <typename Traits>
auto operator!=(unique_handle<Traits> const & left,
unique_handle<Traits> const & right) throw() -> bool
{
return left.get() != right.get();
}
template <typename Traits>
auto operator<(unique_handle<Traits> const & left,
unique_handle<Traits> const & right) throw() -> bool
{
return left.get() < right.get();
}
template <typename Traits>
auto operator>=(unique_handle<Traits> const & left,
unique_handle<Traits> const & right) throw() -> bool
{
return left.get() >= right.get();
}
template <typename Traits>
auto operator>(unique_handle<Traits> const & left,
unique_handle<Traits> const & right) throw() -> bool
{
return left.get() > right.get();
}
template <typename Traits>
auto operator<=(unique_handle<Traits> const & left,
unique_handle<Traits> const & right) throw() -> bool
{
return left.get() <= right.get();
}
struct null_handle_traits
{
using pointer = HANDLE;
static auto invalid() throw() -> pointer
{
return nullptr;
}
static auto close(pointer value) throw() -> void
{
VERIFY(CloseHandle(value));
}
};
struct invalid_handle_traits
{
using pointer = HANDLE;
static auto invalid() throw() -> pointer
{
return INVALID_HANDLE_VALUE;
}
static auto close(pointer value) throw() -> void
{
VERIFY(CloseHandle(value));
}
};
using null_handle = unique_handle<null_handle_traits>;
using invalid_handle = unique_handle<invalid_handle_traits>;