4

私は最近、次のようなC++コードに出くわしました。

class SomeObject
{
private:
    // NOT a pointer
    BigObject foobar;

public:
    BigObject * getFoobar() const
    {
        return &foobar;
    }
};

私はプログラマーになぜfoobarをポインターにしないのかと尋ねたところ、彼はこの方法でメモリの割り当て/割り当て解除について心配する必要がないと言った。私は彼にスマートポインターの使用を検討するかどうか尋ねました、彼はこれも同様に機能すると言いました。

これは悪い習慣ですか?それは非常にハックのようです。

4

6 に答える 6

6

これは完全に合理的であり、決して「ハック」ではありません。ただし、オブジェクトが確実に存在することを示すために参照を返す方がよいと考えられる場合があります。ポインタがnullである可能性があり、使用後にポインタを削除する必要があると考える人もいるかもしれません。

オブジェクトはどこかに存在する必要があり、オブジェクトのメンバーとして存在することは、通常、他の場所に存在することと同じくらい優れています。間接参照を所有するオブジェクトとは別に動的に割り当てることで間接参照のレベルを追加すると、コードの効率が低下し、正しく割り当てが解除されるようにする負担が増えます。

もちろん、メンバー関数は、メンバーへの非参照またはポインターをconst返す場合はできません。constこれは、メンバーにすることのもう1つの利点です。const修飾子onSomeObjectはそのメンバーにも適用されますが、ポインターを持っているだけのオブジェクトには適用されません。

唯一の危険は、誰かがまだオブジェクトへのポインタまたは参照を持っている間にオブジェクトが破壊される可能性があることです。しかし、あなたがそれを管理しても、その危険は依然として存在します。オブジェクトの存続期間が複雑すぎて他の方法で管理できない場合は、スマートポインターがここで役立ちます。

于 2012-09-07T10:28:22.990 に答える
3

参照ではなく、メンバー変数へのポインターを返しています。これは悪いデザインです。クラスはfoobarオブジェクトの存続期間を管理し、そのメンバーへのポインターを返すことにより、クラスのコンシューマーがSomeObjectオブジェクトの存続期間を超えてポインターを使用し続けることができるようにします。また、ユーザーは必要に応じてSomeObjectオブジェクトの状態を変更できます。

代わりに、クラスをリファクタリングして、SomeObjectクラスのfoobarで実行される操作をメソッドとして含める必要があります。

ps。クラスに適切な名前を付けることを検討してください。あなたが定義するとき、それはクラスです。インスタンス化すると、そのクラスのオブジェクトが作成されます。

于 2012-09-07T10:32:40.907 に答える
2

一般に、内部データへのポインタを返すことは理想的とは言えません。クラスが自身のデータへのアクセスを管理するのを防ぎます。しかし、とにかくそれをやりたいのなら、ここでは大きな問題は見られません。メモリの管理を簡素化します。

于 2012-09-07T10:26:04.533 に答える
1

オブジェクトが破壊さgetFoobar()れたときに、から返されたポインタが無効になることを呼び出し元が知っている限り、問題はありません。SomeObjectこのような条件と警告は、古いC++プログラムとフレームワークでは一般的です。

現在の図書館でさえ、歴史的な理由でこれをしなければなりません。たとえばstd::string::c_str、文字列内の内部バッファへのポインタを返します。これは、文字列が破棄されると使用できなくなります。

もちろん、それを大規模または複雑なプログラムで保証することは困難です。最新のC++では、すべてのオブジェクトに可能な限り単純な「値のセマンティクス」を与えることをお勧めします。これにより、すべてのオブジェクトの存続期間は、それを簡単な方法で使用するコードによって制御されます。したがって、裸のポインター、コードの周りに散在する明示的newまたはdelete呼び出しなどがないため、プログラマーがルールに従っていることを手動で確認する必要はありません。

(そして、オブジェクトの存続期間に対する責任の共有を完全に回避できない場合は、スマートポインターに頼ることができます。)

于 2012-09-07T10:26:16.633 に答える
1

これは悪い習慣ですか?それは非常にハックのようです。

です。ポインタが存在する前にクラスがスコープ外になると、メンバー変数は存在しなくなりますが、そのクラスへのポインタはまだ存在します。クラス破壊後のポインタを逆参照しようとすると、未定義の動作が発生する可能性があります。これにより、クラッシュが発生したり、任意のメモリが読み取られてBigObjectとして扱われるバグを見つけるのが困難になる可能性があります。

彼がスマートポインタの使用を検討した場合

スマートポインター、特にstd::shared_ptr<T>ブーストバージョンを使用すると、技術的にここで機能し、クラッシュの可能性を回避できます(共有ポインターコンストラクターを介して割り当てる場合)-ただし、そのポインターの所有者(クラス、または呼び出し元)も混乱しますか?さらに、オブジェクトへのポインターをスマートポインターに追加できるかどうかはわかりません。

これらの2つのポイントはどちらも、クラスからポインターを取得するという技術的な問題を扱っていますが、本当の問題は「なぜ」である必要があります。「なぜクラスからポインタを返すのですか?」のように これが唯一の方法である場合もありますが、多くの場合、ポインタを返す必要はありません。たとえば、変数をその型へのポインターを受け取るCAPIに渡す必要があるとします。この場合、おそらくそのC呼び出しをクラスにカプセル化する方がよいでしょう。

于 2012-09-07T10:28:49.243 に答える
0

ここで2つの無関係な問題:

1)のインスタンスで、必要なインスタンスをどのようSomeObjectに管理しBigObjectますか?の各インスタンスにSomeObject独自のが必要な場合BigObjectBigObjectデータメンバーは完全に合理的です。別のことをしたい場合もありますが、そのような状況が発生しない限り、単純な解決策に固執してください。

2)ユーザーにSomeObject直接アクセスを許可しますBigObjectか?デフォルトでは、適切なカプセル化に基づいて、ここでの答えは「いいえ」になります。しかし、あなたが望むのであれば、それは(1)の評価を変えることはありません。また、必要に応じて、必ずしもポインタを介して行う必要はありません。参照またはパブリックデータメンバーを介して行うこともできます。

(1)の評価を変更する3番目の考えられる問題が発生する可能性があります。

3)ユーザーが、取得したインスタンスの存続期間を超えて使用し続けるSomeObjectインスタンスに直接アクセスできるようにしますか?もしそうなら、もちろんデータメンバーは良くありません。適切な解決策は、または呼び出されるたびに異なる値を返すファクトリである可能性があります。BigObjectSomeObjectshared_ptrSomeObject::getFooBarBigObject

要約すれば:

  • コンパイルされない(getFooBar()戻る必要がconst BigObject*ある)という事実を除けば、これまでのところ、このコードが間違っていると考える理由はありません。それを間違える他の問題が発生する可能性があります。
  • const &よりも戻る方が良いスタイルかもしれませんconst *。どちらを返すかは、データメンバーfoobarである必要があるかどうかには関係ありません。BigObject
  • foobarポインタやスマートポインタを作成することについて「ただ」ということは確かにありません。どちらかBigObjectを指すインスタンスを作成するには、追加のコードが必要になります。
于 2012-09-07T11:19:33.957 に答える