1

私はプロジェクト用のC++ベクトルクラスを書いていますが、いくつかのメソッドをどのように書くのが最善かを決めるのに苦労しています。始める前に、クラスには優れたコピーコンストラクターと代入演算子があると言います(これはすぐに関係します)。このクラスには、どちらのパラメーターも変更されていないことを確認したいときにベクトルを返す一連の静的メソッドがあり、次のようなシグネチャがあります。

Vector* Vector::subtract(const Vector* v, const Vector* u)
{
    double outX = v->myX - u->myX;
    double outY = v->myY - u->myY;
    double outZ = v->myZ - u->myZ;

    return new Vector(outX, outY, outZ);
}

私が抱えている問題は、私がそれを助けることができれば、私はポインタを返したくないということです。代わりに、私はいくつかのテストを行い、私が言うだけで

return Vector(outX, outY, outZ)

次に、次のような結果を割り当てます

Vector foo = Vector::subtract(bar, temp)

コピーが作成され、正常に動作します。ここに私の質問があります:私はコンストラクターを2回(本質的に)呼び出しましたが、それを回避する方法はありますか?次に、このメソッドを次のような別のメソッドの引数として使用する場合

foo.multiply(&Vector::subtract(foo, bar), 5)

それでもコピーが作成されますか、それともVector :: extractメソッドでスコープ外になったポインターを渡すだけですか?

より一般的には、これを行うための最良の(または少なくともより良い方法はありますか)方法は何ですか?

4

4 に答える 4

2

動的に割り当てられたオブジェクトへのポインタ/参照を返すことは、決して道のりではありません。そして明らかな例は、関数が再帰的に呼び出された場合、そのメモリの割り当てを解除する責任があるのは誰ですか?

私は本(Scott Meyers著、Effective C ++ Item#21)から、それを行う唯一の安全な方法はオブジェクト自体を返すことであると読みました。あなたができることは、IIRCで、本質的に一時的な(ある関数から返され、別の関数に割り当てられることなく別の関数に供給される)これらのオブジェクトを排除するコンパイラの仕事を容易にすることです。これらの方法の1つは、匿名にすることでした(別名戻り値の最適化、私に思い出させてくれたインシリコに感謝します)。そのようです

return Vector(outX, outY, outZ);

とは対照的に:

Vector v(outX, outY, outZ);
return v;

したがって、減算メソッドに対して提案された署名は次のようになります。

Vector Vector::subtract(const Vector& v, const Vector& u)
于 2012-09-20T03:12:06.700 に答える
2

コンストラクターを2回(基本的に)呼び出しましたが、それを回避する方法はありますか?

戻り値の最適化について聞いたことがありますか?あなたがしなければならないことは何もありません。コンパイラはおそらくあなたのためにコピーを削除します。これで、C ++ 11を使用し、Vectorクラスがリソースを管理する場合、moveコンストラクターも宣言できるため、コンパイラーがRVOを実行できないと判断した場合に備えて、戻り値が移動されます。ただし、クラスは3つの値しか保持していないように見えます。その場合、コピーは移動と同じくらい効率的です。

&Vector :: extract(foo、bar)

何の&ために?それは間違いでしたか?また、メンバー関数が宣言されていないstaticため、構文が間違っています。とにかく、コピーをsubtract返すと仮定すると、コピーを返し、それをmultiply引数として関数に渡します。

また、別の注意点Vector* Vector::subtract(const Vector* v, const Vector* u) としてVector* Vector::subtract(const Vector& v, const Vector& u)、これにより、減算などの引数を渡すときに構文がよりクリーンになります。

したがって、コードを変更すると、次のようになります。

Vector Vector::subtract(const Vector& v, const Vector& u)
{
    return Vector(v.myX - u.myX, v.myY - u.myY, v.myZ - u.myZ);
}
于 2012-09-20T03:15:05.460 に答える
1

最近のほとんどのコンパイラは、この問題に対処するために、いわゆる戻り値最適化(RVO)を実装します。本当に必要な場合を除いて、ポインタを返さないでください。そうは言っても、おそらく参照を使用する必要があります。全体として、このメソッドを作成する場合は、次のように作成します。

Vector Vector::subtract(const Vector& v, const Vector& u)
{
    return Vector(v.myX - u.myX, v.myY - u.myY, v.myZ - u.myZ);
}
于 2012-09-20T03:14:58.850 に答える
1

戻り型だけでなく、関数への引数でも、ポインターをできるだけ避ける必要があります。可能な限り参照を優先し、必要な場合はポインタを使用してください。

関数を変更して、引数を参照で受け取り、値で返すようにした場合。ドメインで意味がある場合は、演算子のオーバーロードを検討してください(ここではそうだと思います)。

class Vector { ... };
Vector operator+( Vector const& lhs, Vector const& rhs ) {
   return Vector( lhs.x+rhs.x, lhs.y+rhs.y, lhs.z+rhs.y );
}
Vector operator*( Vector const& lhs, Vector const& rhs ) { ... }

次に、操作を自由に連鎖させることができます。

Vector a(...), b(...);
Vector c = (a + b) * c;
于 2012-09-20T03:30:41.570 に答える