2

~and=などの C++ 演算子をオーバーロードするとします。

Foobar& Foobar::operator~() const {
  Foobar *result = new Foobar();
  // compute *result based on *this
  return *result;
}

Foobar& Foobar::operator=(Foobar& arg) {
  // compute *this based on arg
  // return *this for transitivity
  return *this;
}

下位互換性とパフォーマンス上の理由から、演算子は または ポインターでFoobar&はなくを返す必要があります。Foobar

次に、私のクラスのユーザーは次のように記述します。

Foobar obj0, obj1;

obj1 = ~obj0;

からの return~new失われているので、それを行う方法がdeleteないnewので、メモリ リークは発生していませんか? 漏れがある場合、漏れないように設計するにはどうすればよいですか?

4

5 に答える 5

3

これで、new だった ~ からの return が失われたため、その new を削除する方法がないため、メモリ リークが発生していませんか?

はい、現在の実装にはほとんどメモリ リークがあります。または、少なくとも 1 つの可能性が高いです。

漏れがある場合、漏れないように設計するにはどうすればよいですか?

new をそのままにして、ローカル オブジェクトを取得し、値ごとに返さないのはなぜですか?

これが私が意味することです:

Foobar Foobar::operator~() {
  Foobar result;
  // compute *result based on *this
  return result;
}

あなたの例でオブジェクトを動的に割り当てる理由はわかりません。

動的に割り当てられたオブジェクトが本当に必要な場合 (必要な理由がわかりません...)、スマート ポインターを使用することをお勧めします。


編集:

パフォーマンスが問題になる可能性があることを明確にしたので、(n)rvo と @VaughnCato/@Potatoswatter が既に言及したセマンティクスを移動することを指摘したいと思います (彼らの回答を読んでいただければ幸いです)。Want speed?のような記事を読むことをお勧めします。値渡し。 および移動コンストラクター。(n)rvo + move セマンティクスで考えるほど、値による戻りはパフォーマンスの問題ではない可能性があります。ムーブ セマンティクスとプロファイルを実装し、その後最適化を行って、それが本当に問題であるかどうかを確認することをお勧めします。

スマートポインタについて。http://en.cppreference.com/w/cpp/memory/shared_ptrhttp://en.cppreference.com/w/cpp/memory/unique_ptrについて具体的に話していましたが、編集した回答であなたが言ったので完璧な解決策ではない可能性のあるポインターではなく、参照が必要です。あなたが調べたいかもしれない何か。

move/(n)rvo が望ましい結果をもたらさない場合のその他の可能性はかなり不潔であり、私の意見では、プロキシ オブジェクト/グローバル コンテナー (おそらくスマート ポインターと組み合わせたもの) などのようなエラーが発生しやすい方法です。しかし、私はまだこの種のものに慣れていないので、@VaughnCato/@Potatoswatter の回答にコメントしてください。私自身まだまだ初心者なので、ここまでご案内できればと思います。

于 2013-07-15T01:19:51.810 に答える
3

メモリリークが発生することは間違いありません。通常operator~は参照ではなく、値によって返されます。オペランドを変更しないようにするために、演算子も const にする必要があります。このアプローチでは、メモリ リークは発生しません。

Foobar Foobar::operator~() const
{
  Foobar result;
  // compute *result based on *this
  return result;
  // OR
  return Foobar(<stuff to compute result>);
}
于 2013-07-15T02:23:47.807 に答える
1

"パフォーマンス上の理由から、値ごとに返すことはできません。返されるときにクラス全体をコピーするのは遅すぎると考えられます。参照を返さなければなりません。 "

しかし、それは明らかにあなたのコードが行うことです。new を介してタイプの新しいオブジェクトを作成し、それFoobarへの参照を返します。したがって、実際には他のオブジェクトのコピーがあります。コピーを完全に避けたい場合は、式テンプレートのようなものを使用する必要があります。

これは、中間オブジェクトを介して実現できます。

template<class Foo>
struct InvertedFoo
{
  Foo const & to_invert;
  InvertedFoo (Foo const &foo_to_invert) 
    : to_invert(foo_to_invert) { }
};

class Foobar
{
  InvertedFoo<Foobar> operator~ (void) const
  {
    return InvertedFoo<Foobar>(*this);
  }
  Foobar& operator= (InvertedFoo<Foobar> const &arg)
  {
    // compute result based on arg.to_invert
  }
};

これにより、「反転」ロジックが InvertedFoo 引数を取る代入演算子にシフトすることに注意してください。

于 2013-07-15T04:42:28.453 に答える
1

一般に、新しいオブジェクトを割り当ててそれへの参照を返すよりも、値で返す方が高速です。戻り値の最適化 (RVO) または名前付き戻り値の最適化 (NRVO) はコピーを排除し、new を使用してオブジェクトを割り当てることは、思ったほど安くはありません。まだ行っていない場合は、最適化をオンにして両方の方法でプロファイリングし、実行しようとしていることに利点があることを確認する必要があります。

本当に参照を返す必要がある場合、1 つの可能性はグローバル コンテナーを使用することです。

std::list<Foobar *> global_foobars;

Foobar& Foobar::operator~() const {
  Foobar *result = new Foobar();
  global_foobars.push_back(result);
  // compute *result based on *this
  return *result;
}

global_foobarsその後、適切な時期に戻ってオブジェクトを削除できます。

C++11 では、これをもう少しエレガントに行うことができます。

std::list<Foobar> global_foobars;

Foobar& Foobar::operator~() const {
  global_foobars.emplace_back();
  return global_foobars.back();
}
于 2013-07-15T04:43:25.800 に答える
1

はい、new一致するものとペアになっていないとdeleteメモリリークが発生します。

あなたは「簡単」を定義したことがありません。自動削除が必要な場合は、できません。理論的には、デストラクタを定義operator Foobar &して呼び出すスマート ポインタ プロキシ オブジェクトを返すことができます。おそらく、戻り値の型でdeleteある必要があるコードとの下位互換性を管理できますが、それはひどい設計になります。Foobar &オブジェクトはおそらく時期尚早にそれ自体を破壊し、ダングリング参照のみを提供します。

自動的なものに加えて、持っているものを取得rし、戻り値への参照を保持し、例外がスローされた場合でも常にを呼び出すことを忘れないでください。delete &r;ただし、 への呼び出しが成功した場合を除いて、決してoperator ~それを行わないでください。

基本的な問題は、 type に対して 2 つの異なるセマンティック要件があることですFoobar &。そのメモリを解放するには、デストラクタを参照にアタッチする必要がありますが、これはまったく不可能です。

FoobarC++11 の移動セマンティクスや、パフォーマンスの問題を回避する代わりに値で返すプロキシ コンテナー クラスなど、検討すべき実際の解決策は数​​多くあります。

于 2013-07-15T04:39:58.143 に答える