101

私の職場では、このスタイルが広く使用されているのを目にします:-

#include <iostream>

using namespace std;

class A
{
public:
   A(int& thing) : m_thing(thing) {}
   void printit() { cout << m_thing << endl; }

protected:
   const int& m_thing; //usually would be more complex object
};


int main(int argc, char* argv[])
{
   int myint = 5;
   A myA(myint);
   myA.printit();
   return 0;
}

このイディオムを説明する名前はありますか? 大きな複雑なオブジェクトをコピーすることによる大きなオーバーヘッドを防ぐためだと思いますか?

これは一般的に良い習慣ですか?このアプローチに落とし穴はありますか?

4

5 に答える 5

136

このイディオムを説明する名前はありますか?

UML では、集約と呼ばれます。メンバーオブジェクトが参照クラスによって所有されていないという点で、合成とは異なります。C++ では、参照またはポインターを介して、2 つの異なる方法で集計を実装できます。

大きな複雑なオブジェクトをコピーすることによる大きなオーバーヘッドを防ぐためだと思いますか?

いいえ、それはこれを使用する本当に悪い理由です。集約の主な理由は、含まれているオブジェクトが含まれているオブジェクトによって所有されていないため、それらの有効期間がバインドされていないことです。特に、参照されるオブジェクトの有効期間は、参照するオブジェクトよりも長く存続する必要があります。はるかに前に作成された可能性があり、コンテナーの有効期間が終了しても存続している可能性があります。それに加えて、参照されるオブジェクトの状態はクラスによって制御されませんが、外部から変更される可能性があります。参照が でない場合const、クラスはその外部に存在するオブジェクトの状態を変更できます。

これは一般的に良い習慣ですか?このアプローチに落とし穴はありますか?

設計ツールです。場合によっては、それが良いアイデアになることもありますが、そうでない場合もあります。最も一般的な落とし穴は、参照を保持するオブジェクトの有効期間が、参照されるオブジェクトの有効期間を決して超えてはならないということです。参照先のオブジェクトが破棄された後に、囲んでいるオブジェクトが参照を使用すると、未定義の動作が発生します。一般的には、集約よりも合成の方が適していますが、それが必要な場合は、他のどのツールよりも優れたツールです。

于 2012-09-12T12:30:13.363 に答える
46

これは、コンストラクター注入による依存性注入と呼ばれます。クラスAは、そのコンストラクターへの引数として依存性を取得し、依存するクラスへの参照をプライベート変数として保存します。

ウィキペディアに興味深い紹介があります。

const-correctnessについては、次のように書きます。

using T = int;

class A
{
public:
  A(const T &thing) : m_thing(thing) {}
  // ...

private:
   const T &m_thing;
};

ただし、このクラスの問題は、一時オブジェクトへの参照を受け入れることです。

T t;
A a1{t};    // this is ok, but...

A a2{T()};  // ... this is BAD.

追加することをお勧めします (少なくとも C++11 が必要です):

class A
{
public:
  A(const T &thing) : m_thing(thing) {}
  A(const T &&) = delete;  // prevents rvalue binding
  // ...

private:
  const T &m_thing;
};

とにかく、コンストラクターを変更する場合:

class A
{
public:
  A(const T *thing) : m_thing(*thing) { assert(thing); }
  // ...

private:
   const T &m_thing;
};

一時的なへのポインターがないことはほぼ保証されています。

また、コンストラクターはポインターを受け取るためA、渡すオブジェクトの有効期間に注意を払う必要があることがユーザーにとってより明確になります。


多少関連するトピックは次のとおりです。

于 2014-09-15T10:23:16.450 に答える
26

このイディオムを説明する名前はありますか?

この使用法には名前がなく、単に「クラス メンバーとして参照」と呼ばれます。

大きな複雑なオブジェクトをコピーすることによる大きなオーバーヘッドを防ぐためだと思いますか?

はい、また、あるオブジェクトの有効期間を別のオブジェクトに関連付けるシナリオもあります。

これは一般的に良い習慣ですか?このアプローチに落とし穴はありますか?

ご利用状況によります。言語機能を使用することは、「コースの馬を選ぶ」ようなものです。すべての (ほぼすべての) 言語機能が存在することに注意することが重要です。これは、いくつかのシナリオで役立つためです。
参照をクラス メンバーとして使用する場合、注意すべき重要な点がいくつかあります。

  • クラスオブジェクトが存在するまで、参照されたオブジェクトが存在することが保証されていることを確認する必要があります。
  • コンストラクターメンバー初期化子リストでメンバーを初期化する必要があります。ポインターメンバーの場合に可能になる可能性のある遅延初期化を行うことはできません。
  • コンパイラはコピー割り当てを生成しないため、自分で指定operator=()する必要があります。=このような場合にオペレーターが取るべきアクションを判断するのは面倒です。したがって、基本的にあなたのクラスは割り当て不可になります。
  • NULL他のオブジェクトを参照することはできません。再装着が必要な場合は、ポインターの場合のように参照を使用することはできません。

ほとんどの実用的な目的では (メンバーのサイズによるメモリ使用量の増加を本当に懸念している場合を除きます)、ポインターまたは参照メンバーの代わりにメンバー インスタンスを使用するだけで十分です。これにより、余分なメモリ使用量を犠牲にしてでも、参照/ポインター メンバーがもたらす他の問題について心配する必要がなくなります。

ポインターを使用する必要がある場合は、生のポインターではなくスマート ポインターを使用してください。これにより、ポインターを使用すると、人生がはるかに簡単になります。

于 2012-09-12T11:55:02.620 に答える
1

C++ は、クラス/構造体の構成要素を介してオブジェクトの寿命を管理する優れたメカニズムを提供します。これは、他の言語よりも優れた C++ の機能の 1 つです。

ref またはポインターを介して公開されたメンバー変数がある場合、原則としてカプセル化に違反します。このイディオムにより、クラスの消費者は、A がそれを認識したり制御したりすることなく、A のオブジェクトの状態を変更できます。また、消費者は、A のオブジェクトの存続期間を超えて、A の内部状態への参照/ポインターを保持することもできます。これは悪い設計です。代わりに、クラスをリファクタリングして、共有オブジェクトへの参照/ポインターを保持し (それを所有するのではなく)、これらをコンストラクターを使用して設定できます (ライフ タイム ルールを義務付けます)。共有オブジェクトのクラスは、場合によってはマルチスレッド/同時実行をサポートするように設計されている場合があります。

于 2012-09-12T11:54:54.167 に答える
-3

メンバー参照は通常、悪いと見なされます。メンバーポインターと比較して、それらは人生を困難にします。しかし、それは特に珍しいものではなく、特別な名前のイディオムやものでもありません。それは単なるエイリアシングです。

于 2012-09-12T11:38:21.440 に答える