1

多くの場合、C++ プログラムは、リソースの割り当てと解放のためのフリー関数を提供する C ライブラリを処理する必要があります。この例を簡単にするために、 と のような 2 つの C 関数について考えてみget_resource()ますfree_resource()

オブジェクトがその寿命のある時点でリソースを取得し、オブジェクトが破棄されるか、構築中のエラーのために完全に構​​築されていない場合、自動的に解放することを検討してください。

その自動化を得るための理想的/短い/簡単なイディオムは何ですか? アイデアは次のとおりですが、オブジェクトが適切にデフォルトで移動可能になりません。デストラクタ内からメモリを解放したり、コンストラクタでエラーをチェックしてロールバックを実行したりすることを意味しない、より良いものはありますか?

struct Object {
    void* my_get_resource() { // might be called from the constructor or any other point
        return get_resource();
    }

    Object() : up(&resource, &del) { 
        resource = my_get_resource();
        /* init all the stuff, but this init might fail at _many_ points */ 
    }
    //~Object() { if (resource) free_resource(resource); } // I don't like: collides with del and is not called if my_get_resource() is called from the constructor and the init process fails for some reasons

private:
    void *resource = nullptr;
    static void del(void ** ) noexcept {
        if (*resource) { free_resource(resource); }
    }
    unique_ptr < void*, decltype(&del) > up; // RAII: almost good, sadly that makes Object not moveable properly without redefining the move constructor properly


};
4

1 に答える 1

4

どうやらあなたは移動可能なRAIIラッパーが必要です。

次に、移動コンストラクターを定義し、保護またはプライベートコピーコンストラクターとコピー代入演算子を宣言します。現在のVisualC++をサポートする予定がない場合は、コピーコンストラクターとコピー代入演算子を削除済みとして宣言するだけです。

これには、コンストラクターでのエラーのチェックとデストラクタでのクリーンアップが含まれますが、これは要件とは異なります…

デストラクタ内からメモリを解放したり、コンストラクタでエラーをチェックしてロールバックを実行したりすることを意味しない、より良いものはありますか?

簡単に言えば、投稿されたコードで示されているように、要件は一般的にあなたの目標と互換性がありません。

unique_ptr仕事をするために使用する場合でも、コンストラクターでエラーをチェックし、デストラクタでクリーンアップすることで、(非常に非現実的な)要件と直接対立します。

「手動で」物事を始める方法は次のとおりです。

bool hopefully( bool const c ) { return c; }
bool throwX( string const& s ) { throw std::runtime_error( s ); }

class Resource
{
private:
    void* pResource;

    Resource( Resource const& );    // Add "= delete" if all compilers support it.
    Resource& operator=( Resource const& ); // Ditto.

public:
    void* theValue() const { return pResource; }  // Use problem-specific name.

    ~Resource()
    {
        if( pResource != 0 )
        {
            ::freeResource( pResource );
        }
    }

    Resource()
        : pResource( ::getResource() )
    {
        hopefully( pResource != 0 )
            || throwX( "Resource::<init>: getResource failed" );
    }

    Resource( Resource&& other )
        : pResource( other.pResource )
    {
        other.pResource = 0;
    }
};

ムーブ代入演算子を追加できます。

Handleそして、それをクラステンプレートに一般化することができます。


免責事項:テストされていないオフザカフコードであり、コンパイラの手に触れられていません。

于 2012-12-03T00:48:36.847 に答える