web-dev-qa-db-ja.com

(非const)参照によって渡された引数へのポインタの格納

後で使用するために格納することを意図しており、「null」であってはならないオブジェクトを渡すためのインターフェースを設計するとき、引数が参照によって渡されるのか、ポインターとして渡されるのかは常に少し不確かです。

これは私が意味することの例です:

class Foo
{
private:
    Bar* m_bar;

public:
    Foo() : m_bar(nullptr)
    {
    }

    void Register(Bar& bar)
    {
        m_bar = &bar;
        m_bar->Registered();
    }
    // -- OR --
    void Register(Bar* const bar)
    {
        if (bar == nullptr)
        {
            // Error!
        }

        m_bar = bar;
        m_bar->Registered();
    }

    // Some method makes use of the stored pointer later
    void DoSomething()
    {
        if (m_bar == nullptr)
        {
            // Error!
        }

        m_bar->DoOtherThing();
    }
};

これについての私の考えは:

  1. 渡された参照は、DoSomethingが呼び出される前にスコープから外れる可能性がありますが、それは、ポイントされたオブジェクトでも発生する可能性があります。
  2. Pass-by-non-const-referenceバージョンを使用すると、nullのチェックの重複がなくなり、呼び出し側に「何も」を登録できないことを通知します。
  3. Fooで必要とされるため、DoSomethingのコンストラクターで参照を渡すことをお勧めしますが、これがオプションではない場合もあります。
  4. DoSomethingへの参照を直接渡すことをお勧めしますが、これも常に可能とは限りません。

では、そのような個別のsetter/registerメソッドが必要な場合、参照またはポインターを使用する方が明確でしょうか?

PS私は2つの非常によく似た質問があることを知っています 参照として渡すパラメーターをポインターとして格納する-悪い習慣? および 引数C++/Cの参照vs逆参照ポインター 、しかし、どちらも少し異なる問題に関係していると思います。前者は主にconstの(ab)使用を扱い、後者はポインタのstoringについては何も述べていません。 そこに別の質問/答えがあるかもしれません、もしそうなら、これを複製としてマークしてください!

3
Simon Lehmann

この質問の答えにはバイアスが必要だと思うので、難燃性のスーツを準備しています。

まず、私は組み込みの世界で作業しているので、RAIIとスタック割り当てが一般的な方法であることに注意したいと思います。ほぼすべての状況で、明らかな理由により、参照によって何かを渡すことは良いアイデアであると考えています。それはNULLにはできません。ただし、これは主にスタック割り当てを行うためです-したがって、通常はヒープから何かを取り出して渡すときに参照に詰め込みません。このスタイルのプログラミングは、懸念事項1、3、およびあなたがリストした4。

ポインターを渡す場合、2つの理由のいずれかが原因です。1)NULLが本当にオプションである(ややまれ)または2)オブジェクトが渡されたオブジェクトの所有権をオブジェクトが取得していることを通知したい。スタイルとドメインにいくらか下がっていますが、経験則により、エラーが減り、コードの処理内容についてチームのプログラマーと連絡を取ることができました。

2
J Trana

typicalの状況では、nullptrを受け入れるか、タイプの一般的な使用法が値としてでない場合にのみ、ポインターを渡します。これは、たとえば、一部(すべてではない)のインターフェースです。ほとんどの場合、常に(潜在的にconst)参照を使用します。

0
DeadMG

私の頭の問題は、参照とポインタのどちらを使用しても、クラスには個別に作成されたデータのビューがあり、個別に破棄される可能性があり、クラスがそれを知る方法がないということです。

Foo f;
if (something) {
    Bar x;
    // stuff with x
    f.Register(x);
}

f.DoSomething(); // screwed! 

そのような無邪気なコードが何か悪いことをするのは良い兆候ではありません。それはあなたが入っていないあなたの意図の詳細に依存しますが、私はおそらく共有ポインターを使用するでしょう。代替策は、BarのデストラクタがFooに到達してポインタをnullにすることですが、今度はBarはFooへのポインタを必要とし、少しばかげています。共有ポインタが、検討した両方のソリューションの代替手段としてのニーズを意味しないと感じた場合、その理由を説明できますか?

編集:上記で概説したのと同じような状況で、共有ポインターのように寿命を延ばしたくない場合は、有効性を確認して適切に動作させるために、弱いポインターを使用できますが、それはそれですやり過ぎのように見え始めています。

0
Nir Friedman