web-dev-qa-db-ja.com

メンバーデータ内のポインターまたは参照を優先する必要がありますか?

これは、質問を説明するための簡略化された例です。

class A {};

class B
{
    B(A& a) : a(a) {}
    A& a;
};

class C
{
    C() : b(a) {} 
    A a;
    B b; 
};

そのため、BはCの一部を更新する責任があります。lintを介してコードを実行し、参照メンバーについて lint#1725 と言いました。これは、デフォルトのコピーと割り当てを十分に公正に処理することについて述べていますが、デフォルトのコピーと割り当てはポインターでも悪いので、そこにはほとんど利点がありません。

裸のポインターは、そのポインターを削除する責任がある人について不確かに紹介するため、できる限り参照を使用しようとします。オブジェクトを値で埋め込むことを好みますが、ポインターが必要な場合は、ポインターを所有するクラスのメンバーデータでauto_ptrを使用し、オブジェクトを参照として渡します。

通常、ポインターがnullになったり変更されたりする可能性がある場合にのみ、メンバーデータでポインターを使用します。データメンバーの参照よりもポインターを好む他の理由はありますか?

参照は初期化された後に変更されるべきではないため、参照を含むオブジェクトは割り当て可能ではないというのは本当ですか?

126
markh44

参照メンバーは、クラスの実装が実行できることを制限するため(あなたが言及したように、代入演算子の実装を防ぐことを含む)、クラスが提供できるものには何の利点も提供しないため、参照メンバーを避けます。

問題の例:

  • 各コンストラクターの初期化リストで参照を初期化する必要があります。この初期化を別の関数に分解する方法はありません(とにかくC++ 0xまで edit:C++に コンストラクタの委任 )が追加されました
  • 参照を再バインドしたり、nullにすることはできません。これは利点になりますが、コードを変更して再バインドを許可したり、メンバーをnullにしたりする必要がある場合は、メンバーのすべての使用を変更する必要があります
  • ポインターメンバーとは異なり、リファクタリングが必要になる可能性があるため、参照をスマートポインターまたはイテレーターで簡単に置き換えることはできません
  • 参照が使用されるときはいつでも、値型(.演算子など)が、ポインタのように動作します(ぶら下げることができます)-たとえば Googleスタイルガイド がっかりさせます
51
James Hopkin

私の経験則:

  • オブジェクトの寿命を他のオブジェクトの寿命に依存させる場合は、参照メンバーを使用します:有効なインスタンスなしではオブジェクトの生存を許可しないことを明示的に示します別のクラスの-割り当てがなく、コンストラクタを介して参照の初期化を取得する義務があるため。 インスタンスが他のクラスのメンバーであるかどうかについては何も仮定せずにクラスを設計するのに良い方法ですあなたは彼らの生活が他のインスタンスに直接リンクしていると仮定するだけです。クラスインスタンスの使用方法を後で変更できます(新規、ローカルインスタンスとして、クラスメンバーとして、マネージャーのメモリプールによって生成されるなど)。
  • 他の場合はポインターを使用:後でメンバーを変更する場合は、ポインターまたはconstポインターを使用して、ポイントされたインスタンスのみを確実に読み取るようにします。 その型がコピー可能であることになっている場合、参照を使用することはできません。時々、特別な関数呼び出し(init()など)の後にメンバーを初期化する必要があります。ポインタを使用します。 ただし、すべてのメンバー関数でアサートを使用して、誤ったポインター状態をすばやく検出します!
  • オブジェクトのライフタイムを外部オブジェクトのライフタイムに依存させ、その型もコピー可能にする必要がある場合は、ポインターメンバーを使用しますが、コンストラクターで参照引数を使用しますこのオブジェクトのライフタイムは引数のライフタイムに依存しますが、実装ではポインタを使用してコピー可能です。これらのメンバーがコピーによってのみ変更され、タイプにデフォルトのコンストラクターがない限り、タイプは両方の目標を満たすべきです。
138
Klaim

オブジェクトが割り当てや比較などの他のものを許可することはめったにありません。 「Department」、「Employee」、「Director」などのオブジェクトを持つビジネスモデルを検討する場合、ある従業員が他の従業員に割り当てられるケースを想像するのは困難です。

したがって、ビジネスオブジェクトの場合、1対1および1対多の関係を、ポインターではなく参照として記述するのが非常に良いです。

そして、おそらく、1または0の関係をポインターとして説明してもかまいません。

したがって、「割り当てることはできません」と因数分解しません。
多くのプログラマーはポインターに慣れるだけであるため、参照の使用を避けるために引数を見つけます。

ポインターをメンバーとして持つと、使用する前に「念のため」のコメントを付けて、ユーザーまたはチームのメンバーがポインターを何度も何度もチェックするように強制されます。ポインターがゼロになる可能性がある場合、ポインターはおそらくフラグの一種として使用されますが、これはすべてのオブジェクトが独自の役割を果たさなければならないため、悪いことです。

32
Mykola Golubyev
11
ebo

いくつかの重要なケースでは、割り当て可能性はまったく必要ありません。これらは、多くの場合、スコープを離れることなく計算を容易にする軽量のアルゴリズムラッパーです。このようなオブジェクトは、常に有効参照を保持し、コピーする必要がないことを確認できるため、参照メンバーの主要な候補です。

そのような場合は、割り当て演算子(および多くの場合、コピーコンストラクター)を使用不可にしてください(boost::noncopyableまたはそれらをプライベートと宣言します)。

ただし、ユーザーptsはすでにコメントしているため、他のほとんどのオブジェクトには同じことが当てはまりません。ここで、参照メンバーの使用は大きな問題になる可能性があるため、通常は避ける必要があります。

6
Konrad Rudolph

誰もが一般的なルールを配布しているように見えるので、2つ提供します。

  • クラスメンバーとして参照を使用しないでください。私は自分のコードでこれを行ったことはありません(この規則に正しかったことを自分自身に証明することを除いて)。セマンティクスは複雑すぎるため、実際に参照が設計されたわけではありません。

  • 基本型以外の関数にパラメーターを渡すとき、またはアルゴリズムがコピーを必要とするときは、常に、常に参照を使用します。

これらのルールはシンプルであり、私に代わるものでした。他のクラスメンバーとしてスマートポインター(ただし、auto_ptrではなく)を使用するルールを作成します。

6
anon

はい: 参照は初期化された後に変更されるべきではないため、参照を含むオブジェクトは割り当て可能ではないというのは本当ですか?

データメンバーの経験則:

  • 参照を使用しないでください。割り当てを妨げるためです。
  • クラスが削除を担当する場合は、boostのscoped_ptrを使用します(auto_ptrより安全です)
  • それ以外の場合は、ポインターまたはconstポインターを使用します
4
pts

通常、ポインターがnullになったり変更されたりする可能性がある場合にのみ、メンバーデータでポインターを使用します。データメンバーの参照よりもポインターを好む他の理由はありますか?

はい。コードの可読性。ポインターを使用すると、メンバーが含まれているオブジェクトではなく、参照(皮肉なことに:))であることがより明確になります。昔ながらのやり方だと思う人もいますが、それでも混乱や間違いを防ぐだけだと思います。

2

誰があなたのクラスから派生するのか、彼らが何をしたいのかわからないので、参照データのメンバーに対してアドバイスします。彼らは参照されたオブジェクトを利用したくないかもしれませんが、有効なオブジェクトを提供することを強制した参照です。参照データメンバーの使用を停止するのに十分なだけ、これを自分で行いました。

2
StephenD

質問を正しく理解したら...

ポインターの代わりに関数パラメーターとして参照:あなたが指摘したように、ポインターは、ポインターのクリーンアップ/初期化の所有者を明確にしません。ポインタが必要な場合は共有ポイントを優先します。これはC++ 11の一部であり、データの有効性がデータへのポインタを受け入れるクラスのライフタイムを通じて保証されない場合はweak_ptrです。 C++ 11の一部でもあります。関数パラメーターとして参照を使用すると、参照がnullでないことが保証されます。これを回避するには、言語機能を破壊する必要があります。ルーズキャノンコーダーは気にしません。

メンバー変数の参照:データの有効性に関しては上記と同じです。これは、ポイントされたデータが参照され、有効であることを保証します。

変数の有効性の責任をコード内の以前のポイントに移動すると、後のコード(この例ではクラスA)がクリーンアップされるだけでなく、使用する人にも明確になります。

少しわかりにくい例では(より明確な実装を探しています)、BがAのメンバーであるため、Bが使用するAはBの存続期間中保証されます。そして(おそらく)より明確です。

そして、念のために(これはコードのコンテキストでは意味をなさない可能性が低いため)、参照されていない非ポインターAパラメーターを使用する別の代替手段は、Aをコピーし、パラダイムを無用にします-Iこれを代替手段として意味するとは思わないでください。

また、これにより、参照されているデータを変更できないことも保証されます。この場合、ポインターを変更できます。 constポインターは、データへの参照/ポインターが変更可能でない場合にのみ機能します。

ポインターは、BのAパラメーターの設定が保証されていない場合、または再割り当てできる場合に役立ちます。そして時々、弱いポインターは実装には冗長すぎて、かなりの数の人がweak_ptrが何であるかを知らないか、単に嫌いです。

この答えをバラバラにしてください:)

0
Kit10