web-dev-qa-db-ja.com

参照渡しパラメーターをポインターとして格納する-悪い習慣?

私は最近、使用を強制されたAPIで次のパターンに遭遇しました。

class SomeObject
{
public:
    // Constructor.
    SomeObject(bool copy = false);

    // Set a value.
    void SetValue(const ComplexType &value);

private:
    bool         m_copy;
    ComplexType *m_pComplexType;
    ComplexType  m_complexType;
};

// ------------------------------------------------------------

SomeObject::SomeObject(bool copy) :
  m_copy(copy)
{
}

// ------------------------------------------------------------

void SomeObject::SetValue(const ComplexType &value)
{
    if (m_copy)
        m_complexType.assign(value);
    else
        m_pComplexType = const_cast<ComplexType *>(&value);
}

このパターンの背景には、エンコードしてからTCPソケットに送信する前のデータを保持するために使用されます。コピーの奇妙さは、クラスSomeObjectを効率的にするように設計されていますエンコードする必要があるまでオブジェクトへのポインターを保持するだけでなく、SomeObjectのライフタイムがComplexTypeのライフタイムを超えた場合に値をコピーするオプションも提供します。

ただし、次の点を考慮してください。

SomeObject SomeFunction()
{
    ComplexType complexTypeInstance(1);  // Create an instance of ComplexType.

    SomeObject encodeHelper;
    encodeHelper.SetValue(complexTypeInstance); // Okay.

    return encodeHelper;

    // Uh oh! complexTypeInstance has been destroyed, and
    // now encoding will venture into the realm of undefined
    // behaviour!
}

デフォルトのコンストラクターを使用したため、これをつまずきました。これにより、メッセージが空白としてエンコードされました(未定義の動作のまとまりにより)。原因を特定するには絶対年齢がかかりました!

とにかくこれはこのようなものの標準的なパターンですか?SetValueメソッドをオーバーロードして、不足しているポインターを受け入れるようにするのと比べて、この方法で行うことには利点がありますか?

ありがとう!

4
Karl Nicoll

はい(複数のプロジェクトで見たように)ポインターとして参照が使用されるのが標準であり、オブジェクトの存続期間の問題を隠すので、そうではありません。

そして、constキャストは不要であり、完全に恐ろしいものです。少なくとも、渡されるのはconst refではなく、refである必要があります。

改善の1つの方法を提案するように、const refとコピーを取得する(同時にコピーフラグを設定する)2つの異なる関数を使用することをお勧めします。もう1つはポインターを取得するため、ユーザーは明示的に非コピー動作を呼び出します。

異なる名前(たぶん、CopyValueとReferenceValue)を持つ2つの異なるメソッドがあり、さらに2つの異なる動作を実行する2つの異なるサブクラスクラスを持つ基本クラスがあるほうがよいでしょう。

2

別の解決策は、参照を受け取るメソッドをポインタを受け取るメソッドに置き換えることです。次に、呼び出し元はnew演算子を使用してオブジェクトを作成し、unique_ptrに格納します(auto_ptrと言いますが、非推奨になった理由は、ここで使用することになるためです)。このようにして、コピーを作成したり、オブジェクトの破壊を心配したりすることはありません。

簡単なマッシュアップは

class SomeObject
{
public:
    // Constructor.
    SomeObject();

    // Set a value.
    void SetValue(unique_ptr<ComplexType> pValue);

private:
    std::unique_ptr<ComplexType> m_pComplexType;
};

// ------------------------------------------------------------

void SomeObject::SetValue(std::unique_ptr<ComplexType> pValue)
{
    m_pComplexType = std::move(pValue);
}

..これは次のように使用できます。

SomeObject SomeFunction()
{
    std::unique_ptr<ComplexType> pobjComplexTypeInstance= new ComplexType(1);  // Create an instance of ComplexType and store it in a unique_ptr

    SomeObject encodeHelper;
    encodeHelper.SetValue(pobjComplexTypeInstance); // Pass the unique_ptr, it will be copied inside the function

    return encodeHelper;
}
1
dotbugfix