30

特に C 関数をクラスにラップする場合は、const メンバー変数のアイデアが気に入っています。コンストラクターは、オブジェクトの存続期間全体にわたって有効なリソース ハンドル (ファイル記述子など) を取得し、最終的にデストラクタがそれを閉じます。(それがRAIIの考え方ですよね?)

しかし、C++0x ムーブ コンストラクターを使用すると、問題が発生します。デストラクタは「アンロードされた」オブジェクトでも呼び出されるため、リソース ハンドルのクリーンアップを防ぐ必要があります。メンバー変数は const であるため、値 -1 または INVALID_HANDLE (または同等の値) を割り当てて、何もしないことをデストラクタに示す方法がありません。

オブジェクトの状態が別のオブジェクトに移動した場合、デストラクタが呼び出されないようにする方法はありますか?

例:

class File
{
public:
    // Kind of "named constructor" or "static factory method"
    static File open(const char *fileName, const char *modes)
    {
        FILE *handle = fopen(fileName, modes);
        return File(handle);
    }

private:
    FILE * const handle;

public:
    File(FILE *handle) : handle(handle)
    {
    }

    ~File()
    {
        fclose(handle);
    }

    File(File &&other) : handle(other.handle)
    {
        // The compiler should not call the destructor of the "other"
        // object.
    }

    File(const File &other) = delete;
    File &operator =(const File &other) = delete;
};
4

5 に答える 5

25

これが、前述のメンバー変数を宣言してはならない理由ですconstconstメンバー変数は通常、何の役にも立ちません。ユーザーに を変更させたくない場合はFILE*、そのための関数を提供しないでください。誤って変更するのを防ぎたい場合は、関数をマークしてくださいconst。ただし、メンバー変数自体を作成しないでください。移動またはコピーのセマンティクスを使用し始めると、おかしなconstことになるからです。

于 2011-06-11T17:31:25.103 に答える
9

いいえ、これを行う方法はありません。constである変数に本当に接続している場合はhandle、破棄が何かを行う必要があるかどうかを示す非 const フラグ メンバー変数を用意することをお勧めします。

于 2011-06-11T17:29:43.183 に答える
4

実は、私も今日この問題に遭遇しました。「できません」と「shared_ptr /参照カウントを使用する」を受け入れたくないので、さらにグーグルで調べて、この基本クラスを思いつきました:

class Resource
{
private:
     mutable bool m_mine;

protected:
    Resource()
    : m_mine( true )
    {
    }

    Resource(const Resource&)       = delete;
    void operator=(const Resource&) = delete;

    Resource(const Resource&& other)
    : m_mine( other.m_mine )
    {
        other.m_mine = false;
    }

    bool isMine() const
    {
        return m_mine;
    }
};

すべてのメソッドとコンストラクターは保護されているため、使用するには継承する必要があります。可変フィールドに注意してください。これは、子孫がクラスの const メンバーになることができることを意味します。例えば、

class A : protected Resource
{
private:
    const int m_i;

public:
    A()
    : m_i( 0 )
    {
    }

    A( const int i )
    : m_i( i )
    {
    }

    A(const A&& a)
    : Resource( std::move( a     ) )
    , m_i     ( std::move( a.m_i ) ) // this is a move iff member has const move constructor, copy otherwise
    {
    }

    ~A()
    {
        if ( isMine() )
        {
            // Free up resources. Executed only for non-moved objects
            cout << "A destructed" << endl;
        }
    }
};

A のフィールドを const にすることができるようになりました。私は protected を継承したので、ユーザーが誤って A を Resource にキャストすることはできません (または非常に喜んでハックすることはできません) が、A はまだ最終的なものではないため、これから継承することができます (Resource から継承する正当な理由はたとえば、読み取りアクセスと読み書きアクセスを別々にする場合など)。これは、保護された継承が設計に問題があることを自動的に意味しない非常にまれなケースの 1 つです。ただし、理解するのが難しい場合は、パブリック継承を使用することもできます。

次に、次のものがあると仮定しますstruct X

struct B
{
    const A m_a;
    const X m_x;

    B(const A&& a, const X& x) // implement this way only if X has copy constructor; otherwise do for 'x' like we do for 'a'
    : m_a( std::move( a ) )
    , m_x(            x   )
    {
    }

    B( const B&& b )
    : m_a( std::move( b.m_a ) )
    , m_x( std::move( b.m_x ) ) // this is a move iff X has move constructor, copy otherwise
    {
    }

    ~B()
    {
        cout << "B destructed" << endl;
    }
};

B のフィールドは const にもなり得ることに注意してください。Move コンストラクターは const です。型に適切な移動コンストラクターがある場合、ヒープに割り当てられたメモリはオブジェクト間で共有できます。

于 2016-06-10T19:46:51.463 に答える
4

移動コンストラクターを実装する一般的な方法は、移動するインスタンスのメンバーをゼロにするか、無効にすることです (簡単な例については、 MSDNを参照してください)。したがってconst、移動セマンティクスの目標と互換性がないため、ここでは使用しないでください。

于 2011-06-11T17:30:16.940 に答える
-1

参照カウントは、問題を解決する標準的なアプローチです。クラスに参照カウントを追加することを検討してください。手動で行うか、boost shared_ptr などの既存のツールを使用します。

于 2011-06-11T17:33:03.977 に答える