Vector3はVector2を継承する必要がありますか?


18

私はクラスのカップル作成していますVector2(X&Y)をし、Vector3(X、Y&Z)が、私はするかどうかわかりませんVector3から継承するVector2、またはにするかどうかのメンバ変数を再実装するm_xm_y、再び?それぞれの長所と短所は何ですか(継承と再定義)。

編集: C ++(VS2010)を使用しています。


1
なぜn次元ベクトルの一般的なベクトルクラスを記述し、(必要に応じて)vector2およびvector3クラスを継承しないのですか。また、一般クラスのテンプレートを使用し、整数ベクトルと浮動小数点ベクトルのバージョンを継承することもできます。編集:最適化された数学ライブラリを使用しないのはなぜですか?
danijar

5
想像力の無いストレッチ「のVector3はベクトル2である」、彼らは親VectorNから継承しかし、両方の可能性によって
WIM

1
ええ、しかし、おそらく仮想テーブルが必要になります。これは、実行時間とメモリのコストが問題になる場合の1つです。理想的には、メモリに関する限り、a Vector3は3 である必要がfloatsあります。それが不可能だと言っているのではなく、実稼働エンジンでそれを見たことがないというだけです。
ローランクービドゥ

2
はい、そう思います。あなたが他に何も必要としない限りまでfloats。ヤグニ、キッス、そういったものすべて。Vector2Vector3およびVector4継承なしfloatsで、ゲームエンジンの実際のデファクトスタンダードのみです。
ローランクーヴィドゥ

1
私はあなたがtypedef float real;;)を意味することを望みます。
マークイングラム

回答:


47

いいえ、できません。あなたが相続から使用しているだろう唯一のものはあるxyコンポーネント。Vector2クラスで使用されるメソッドは、クラスでは有用ではありません。Vector3異なる引数を取り、異なる数のメンバー変数で操作を実行する可能性があります。


+1、ポップアップにもっと注意を払う必要があるので、冗長なものを書きません。
マツマン

8
古典的な継承の乱用。Aは、Vector3IS-NOT-A Vector2(SOそれはない継承する)が、AppleIS-A Fruit(それが継承することができるようにします)。心を十分にひねり、Vector3HAS-Aを使用Vector2している場合でも、パフォーマンスの低下とコーディングの難しさからVector3、との完全に別個のクラスを作成することになりVector2ます。
ボボボボ

しかし、2次元ベクトルと3次元ベクトルを継承するn次元ベクトルクラスを書くことができます(私の意見ではそうすべきです)。
ダニジャー

8

C ++でできることは奇妙です(言語を指定しなかったため、ほとんどの場合、これが役立つとは思わないが、この答えは主に代替案を見るのが良いと思うためです)。

テンプレートを使用すると、次のようなことができます。

template <class T, class S, int U>
class VectorN
{
    protected:
        int _vec[U];
    public:
        S& operator+=(const S c)
        {
            for(int i = 0; i < U; i++)
            {
                _vec[i] += c.at(i);
            }
            return (S&)*this;
        }
        int at(int n) const
        {
            return _vec[n];
        }
};

template <class T>
class Vec2 : public VectorN<T,Vec2<T>,2>
{
    public:
        T& x;
        T& y;
        Vec2(T a, T b) : x(this->_vec[0]), y(this->_vec[1])
        {
            this->_vec[0] = a;
            this->_vec[1] = b;
        }
};

template <class T>
class Vec3 : public VectorN<T,Vec3<T>,3>
{
    public:
        T& x;
        T& y;
        T& z;
        Vec3(T a, T b, T c) : x(this->_vec[0]), y(this->_vec[1]), z(this->_vec[2])
        {
            this->_vec[0] = a;
            this->_vec[1] = b;
            this->_vec[2] = c;
        }
};

これは次のように使用できます。

int main(int argc, char* argv[])
{

    Vec2<int> v1(5,0);
    Vec2<int> v2(10,1);

    std::cout<<((v1+=v2)+=v2).x;
    return 0;
}

私が言ったように、これは有用ではないと思います。そして、ドット/正規化/他のものを実装して、任意の数のベクトルでジェネリックにしようとすると、おそらくあなたの人生を複雑にします。


すべての角括弧を行います-うん、すべての総称性はほとんどあなただけの標準的な3つの浮動小数点要素を持つ3ベクトル必要な時間の、素敵なようでVector3f vかなり多くbloatyVector3<float> v
bobobobo

@boboboboはい、同意します。私のベクトルクラスは通常、親のないvec2とvec3ですが、それでもテンプレートにします。Vector3 <float>を書くのが面倒なら、いつでもtypedefすることができます
ルークB.

..そして今、Cプログラマーの議論..「しかし、テンプレートを使用するためのコンパイル時間の増加はどうですか?」この場合、本当に価値がありますか?
ボボボボ

@boboboboコンパイル時間に問題はありませんでした:P、しかし、コンパイル時間が問題になるプロジェクトに取り組んだことはありません。整数が必要な場合、コンパイル時間はフロートを使用しないことを正当化すると主張することができます。
ルークB。

@bobobobo明示的なインスタンス生成を使用し、ヘッダーにインラインファイルを含めない場合、コンパイル時間は変わりません。さらに、テンプレート山形ブラケットの肥大化はわずか1つtypedefです。
サマールサ

7

やったときに速度に関係なく、最初の質問は、あなたは自問しなければならないすべてのあなたが多形、それらを使用しているつもりなら継承があります。より具体的には、a Vector3を使用して自分自身をaであるかのように見ることができる状況がありますVector2(これを継承することにより、Vector3が「is-a」Vector2であることを明示的に言っています)。

そうでない場合は、継承を使用しないでください。コードを共有するために継承を使用しないでください。それがコンポーネントと外部関数の目的です。とにかくそれらの間でコードを共有するわけではありません。

そうは言っても、sをs に変換する 簡単な方法が必要な場合があります。その場合、暗黙的にaを切り捨てる演算子オーバーロードを記述できます。しかし、継承すべきではありません。 Vector3Vector2Vector3Vector2


おかげで、それが問題を浮き彫りにしたと思います。私はこれを「コード共有」の観点から見ていました(つまり、XとYの値を「再入力」する必要はありません)。
マークイングラム

+1の素晴らしい答え、異なるサイズのベクトル間に多態的な使用はありません。
ルークB。

これは私が自分の答えに加えようとしていた最大のものです-確かに+1。(多態性欲しいと想像できる奇妙な状況があります-例えば、距離チェック、パスなどのようなものが2Dで標準的に行われたい2.5D「ハイトマップ」ゲームですが、オブジェクトの3D座標を提供する必要があります)
スティーブンスタドニッキー

@LukeB。OPの場合Vector2、ベースから継承するが、ベースから継承する理由はないと思われることに同意しVector<N>ますか?それは完全に理にかなっています。さらに、なぜ継承は自動的に多態的な振る舞いを意味するのですか?C ++の最大の利点の1つは、ゼロコストの継承が可能なことです。追加する必要はありません任意のベースに(仮想デストラクタを含む)仮想メソッドをVector<N>クラス。
サマールサ

5

いいえ、すべてのメソッドをオーバーライドする必要があるため、実際に継承する方法は使用できません。

どちらかといえば、両方ともVectorインターフェイスを実装できます。ただし、Vector2とVector3の間に/ sub / dot / dstを追加したくないので、望ましくない副作用があります。また、異なるパラメーターなどを使用するのは面倒です。
そのため、この場合、継承/インターフェースの長所を実際に見ることはできません。

例としては、Libgdxフレームワークがあります。この場合、Vector2Vector3は、同じタイプのメソッドを持っていること以外は互いに関係ありません。


2

SIMD アレイを使用する場合は、おそらく最適です。演算子のオーバーロードを引き続き使用する場合は、インターフェイス/ミックスインを使用して基になる配列にアクセスすることを検討できます。たとえば、ここには(未テスト)のみがある開始点がありAddます。

X/ Y/を提供していないことに注意してください。他の人が指定したのと同じ理由でZ、各VectorXクラスはこのクラスから直接継承します。それでも、私は配列をベクトルとして野生で何度も使用しています。

#include <xmmintrin.h>

class Vector
{
public:
    Vector(void)
    {
        Values = AllocArray();
    }

    virtual ~Vector(void) 
    { 
        _aligned_free(Values);
    }

    // Gets a pointer to the array that contains the vector.
    float* GetVector()
    {
        return Values;
    }

    // Gets the number of dimensions contained by the vector.
    virtual char GetDimensions() = 0;

    // An example of how the Vector2 Add would look.
    Vector2 operator+ (const Vector2& other)
    {
        return Vector2(Add(other.Values));
    }

protected:
    Vector(float* values)
    {
        // Assume it was created correctly.
        Values = values;
    }

    // The array of values in the vector.
    float* Values;

    // Adds another vector to this one (this + other)
    float* Add(float* other)
    {
        float* r = AllocArray();

#if SSE
        __m128 pv1 = _mm_load_ps(Values);
        __m128 pv2 = _mm_load_ps(other);
        __m128 pvr = _mm_load_ps(r);

        pvr = _mm_add_ps(pv1, pv2);
        _mm_store_ps(r, pvr);

#else
        char dims = GetDimensions();
        for(char i = 0; i < dims; i++)
            r[i] = Values[i] + other[i];
#endif

        return r;
    }

private:

    float* AllocArray()
    {
        // SSE float arrays need to be 16-byte aligned.
        return (float*) _aligned_malloc(GetDimensions() * sizeof(float), 16);
    }
};

免責事項:私のC ++は使い物にならないかもしれません。使用してからしばらく経ちました。


ちょっと待って私が開いたバグは本当にバグではない_aligned_mallocという意味ですか?
ボボボボ

ポインタキャストを使用して値を__m128レジスタに取得するのでは_mm_loadu_psなく、代わりに使用する必要があります。適切なサンプルクラスは、「vectorclass.zip」の下にあります
-bobobobo

@bobobobo編集で最善を尽くします-特記事項に注意してください;)。
ジョナサンディキンソン

@bobobobo _mm_loadu_psは、その構造を使用してうまく機能するはずです(そうで_mm_load_psない場合)。私もあなたの提案を追加しました-私が間違った木をbarえていると感じたら、質問を自由に編集してください(C [++]を使ってからしばらく経ちました)。
ジョナサンディキンソン

1

Vec3をVec2から継承すること、またはおそらく両方を単一のVectorクラスから継承することに対するもう1つの重大な欠点:コードは多くのことを行います多くの場合、タイムクリティカルな状況でのベクトルの操作、およびこれらの操作のすべてが可能な限り高速であることを確認することは非常に重要です-他の多くのオブジェクトではない非常に普遍的または低レベルです。優れたコンパイラーは、継承のオーバーヘッドをフラット化するために最善を尽くしますが、それでもコンパイラーへの依存度は自分が望んでいる以上です。代わりに、できるだけオーバーヘッドの少ない構造体として構築し、おそらくそれらを使用する関数のほとんど(実際には役に立たないoperator +のようなものを除く)を、構造体。通常、早期の最適化が推奨されますが、理由はありますが、


1
-1理由:C ++ではクラスと構造体のみがアクセス許可の意味を持ち、OPは言語を指定しなかったため、非仮想メンバー関数は非メンバー関数と同じパフォーマンスの意味を持ち、仮想メンバー関数(潜在的に懸念している問題)は、継承を利用するだけではなく、作成した場合にのみ存在します。

2
@JoshPetrieすべての面で有効なポイント。私はC / C ++に「デフォルト」する傾向があるので、そのレンズを通して質問を見ました。私はない、あなたを気にし、合法的なパフォーマンス(だけでなく、概念的な)相続ルートを行っていない理由があると信じて、私は、特定の詳細に多くの改善の余地があります。これを再検討して、より良い会計を提供できるかどうかを確認します。
スティーブンスタドニッキー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.