0

RAII機能を提供するラッパークラスの設計に取り組んでいます。元の使用例は次のとおりです。

void* tid(NULL);
OpenFunc(&tid);
CloseFunc(&tid);

新しいラッパー クラスを導入した後、将来の使用方法は次のようになると思います。

void* tid(NULL);
TTTA(tid);

また

TTTB(tid);

質問:

どちらの実装が優れていますTTTAか? TTTBまたは、それらはすべて悪いので、より良いものを紹介してください。

私が懸念していることの 1 つは、リソースが割り当てられた後、idがクラス外で、TTTAまたはが破棄さTTTBれるまでアクセスされることです。id私の理解に基づいて、私のデザインには副作用があってはなりません。

ありがとうございました

class TTTA : boost::noncopyable
{
public:
    explicit TTTA(void *id)
        : m_id(id)
    {
        OpenFunc(&m_id); // third-party allocate resource API
    }

    ~TTTA()
    {
        CloseFunc(&m_id); // third-party release resource API
    }   
private:
    void* &m_id; // have to store the value in order to release in destructor
}

class TTTB : boost::noncopyable
{
public:
    explicit TTTB(void *id)
        : m_id(&id)
    {
        OpenFunc(m_id); // third-party allocate resource API
    }

    ~TTTB()
    {
        CloseFunc(m_id); // third-party release resource API
    }   
private:
    void** m_id; // have to store the value in order to release in destructor
}

// パスイン ポインターの比較

class TTTD
{
public:
    TTTD(int* id)    // Take as reference, do not copy to stack.
        : m_id(&id)
    {
        *m_id = new int(40);
    }

private:
    int** m_id; 
};


class TTTC
{
public:
    TTTC(int* &id)    
        : m_id(id)
    {
        m_id = new int(30);
    }

private:
    int* &m_id; 
};

class TTTB
{
public:
    TTTB(int* id)    
        : m_id(id)
    {
        m_id = new int(20);
    }

private:
    int* &m_id; 
};

class TTTA
{
public:
    TTTA(int** id)    
        : m_id(id)
    {
        *m_id = new int(10);
    }

private:
    int** m_id; 
};


int main()
{
    //////////////////////////////////////////////////////////////////////////
    int *pA(NULL);
    TTTA a(&pA);
    cout << *pA << endl; // 10

    //////////////////////////////////////////////////////////////////////////
    int *pB(NULL);
    TTTB b(pB);
    //cout << *pB << endl; // wrong

    //////////////////////////////////////////////////////////////////////////
    int *pC(NULL);
    TTTC c(pC);
    cout << *pC << endl; // 30

    //////////////////////////////////////////////////////////////////////////
    int *pD(NULL);
    TTTD d(pD);
    cout << *pD << endl; // wrong
}
4

3 に答える 3

5

どちらも悪い意味で壊れます。

TTTA は、スタックに格納されている変数 (パラメーター ID) への参照を格納します。
TTTB は、スタックに格納されている変数へのポインターを格納します。

どちらの場合も、コンストラクターが戻ると、変数はスコープ外になります。

編集:値を変更可能にしたいので、最も簡単な修正は、ポインターを参照として取得することです。これにより、ポインタを非参照パラメータとして取得したときに作成されたローカル コピーではなく、TTTC が実際のポインタを参照するようになります。

class TTTC : boost::noncopyable
{
public:
    explicit TTTA(void *&id)    // Take as reference, do not copy to stack.
        : m_id(id)
...
private:
    void* &m_id; // have to store the value in order to release in destructor
}

バージョンを壊す簡単なテストは、printメソッドをクラスに追加して、ポインター値を出力して実行することです。

int main() {

  void* a = (void*)0x200;
  void* b = (void*)0x300;

  {
    TTTA ta(a);
    TTTA tb(b);

    ta.print();
    tb.print();
  }
}

私のマシンでは、TTTA と TTTB の両方が両方の値を 0x300 として出力します。もちろん、結果は本当に UB です。そのため、結果は異なる場合があります。

于 2013-05-21T19:15:22.547 に答える
2

なぜあなたtidはまったくですか?クライアントに情報が漏れており、使用時間が 2 倍になります (1 行ではなく 2 行):

class tttc {
    void* id;

public:

    tttc() {
        OpenFunc(&id);
    }

    ~tttc() {
        CloseFunc(&id);
    }

    tttc(tttc const&) = delete;
    tttc& operator =(tttc const&) = delete;
};

このクラスはコピーを禁止していることに注意してください。あなたのソリューションは 3 つのルールを破っています。

外部から へのアクセスが必要な場合はid、内部で変換を提供しますtttc

void* get() const { return id; }

または、絶対に必要な場合は、暗黙的な変換を介して:

operator void*() const { return id; }

(ただし、暗黙的な変換は型システムを弱体化させ、バグの診断が困難になる可能性があるため、慎重に使用してください。)

そしてstd::unique_ptr、標準ライブラリには、カスタムのデリータを使用して実際に同じことを達成し、さらに 3 つのルールを適切に実装するものがあります。

于 2013-05-21T19:12:49.037 に答える
2

それを完全に包むのはどうですか?この方法では、2 つの変数のライフサイクルの管理について心配する必要はありませんが、1 つのみです。

class TTTC
{
    void* m_id;
public:
    TTTC()
        : m_id(nullptr)
    {
        OpenFunc(&m_id); // third-party allocate resource API
    }

    TTTC(TTTC const&) = delete; // or ensure copying does what you expect

    void*const& tid() const { return m_id; }

    ~TTTC()
    {
        CloseFunc(&m_id); // third-party release resource API
    }
};

それを使用すると、それ自体が単純になります。

TTTC wrapped;
DoSomethingWithTid(wrapped.tid());
于 2013-05-21T19:13:22.363 に答える