Haruskah Vector3 mewarisi dari Vector2?


18

Saya membuat beberapa kelas Vector2(X & Y) dan Vector3(X, Y & Z), tetapi saya tidak tahu apakah akan Vector3mewarisi Vector2, atau apakah akan mengimplementasikan kembali variabel anggota m_xdan m_ylagi? Apa pro dan kontra dari masing-masing pihak (warisan vs redefinisi).

Sunting: Saya menggunakan C ++ (VS2010).


1
Mengapa tidak menulis kelas vektor umum untuk vektor dimensi n dan kemudian (jika perlu) mewarisi kelas vektor2 dan vektor3. Anda juga dapat menggunakan templat untuk kelas umum dan versi bawaan untuk vektor integer dan vektor float juga. Sunting: Mengapa Anda tidak menggunakan perpustakaan matematika yang dioptimalkan?
danijar

5
Tanpa imajinasi "Vector3 is a Vector2", mereka berdua bisa mewarisi dari orangtua VectorN
wim

1
Ya, tapi kemudian Anda mungkin akan membutuhkan tabel virtual dan itulah salah satu kasus di mana runtime & biaya memori bisa menjadi masalah. Idealnya, Vector3seharusnya hanya 3 floatssejauh memori yang bersangkutan. Bukan mengatakan itu tidak mungkin, hanya saja saya belum pernah melihatnya di mesin produksi.
Laurent Couvidou

2
Ya, saya pikir begitu. Sampai selama Anda tidak butuh hal lain itu floats. Anda tahu, YAGNI, KISS, semua itu. Vector2, Vector3dan Vector4tanpa warisan dan floatshanya benar-benar standar de facto di mesin game.
Laurent Couvidou

1
Saya harap Anda maksud typedef float real;;).
Mark Ingram

Jawaban:


47

Tidak seharusnya tidak. Satu-satunya hal yang akan Anda gunakan dari warisan adalah xdan ykomponen. Metode yang digunakan di Vector2kelas tidak akan berguna di Vector3kelas, mereka kemungkinan akan mengambil argumen yang berbeda dan melakukan operasi pada sejumlah variabel anggota yang berbeda.


+1, saya harus lebih memperhatikan popup jadi saya tidak menulis hal-hal yang berlebihan.
Matsemann

8
Warisan klasik terlalu sering digunakan . A Vector3IS-NOT-A Vector2(jadi seharusnya tidak mewarisi), tetapi AppleIS-A Fruit(jadi mungkin mewarisi). Jika Anda cukup memutar pikiran, memiliki Vector3HAS-A Vector2di dalamnya, tetapi kehilangan kinerja dan kesulitan coding berarti Anda akan menulis kelas yang benar-benar terpisah untuk Vector3dan Vector2.
bobobobo

Tetapi Anda dapat (menurut saya harus) menulis kelas vektor n-dimensi untuk mewarisi vektor 2d dan vektor 3d dari itu.
danijar

8

Ada hal aneh yang dapat Anda lakukan dengan C ++ (Anda tidak menentukan bahasa, dan jawaban ini sebagian besar karena saya pikir senang melihat alternatif, meskipun saya tidak benar-benar percaya ini berguna dalam kebanyakan kasus.)

Menggunakan templat Anda dapat melakukan sesuatu seperti ini:

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;
        }
};

dan ini bisa digunakan seperti ini:

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

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

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

Seperti yang saya katakan, saya tidak berpikir ini berguna, dan itu mungkin akan mempersulit hidup Anda ketika Anda mencoba menerapkan dot / menormalkan / hal-hal lain dan mencoba untuk menjadi generik dengan sejumlah vektor.


Ya, semua genericness tampak bagus, hanya sebagian besar waktu Anda hanya memerlukan 3-vektor standar dengan 3 komponen floating point - semua kurung sudut akan membuat Vector3f vsedikit lebih kembungVector3<float> v
bobobobo

@obobobo Ya, saya setuju. Kelas vektor saya biasanya vec2 dan vec3 tanpa orangtua, tetapi masih membuatnya templat. Jika menulis Vector3 <float> mengganggu Anda, Anda selalu dapat mengetikkannya
Luke B.

..Dan sekarang argumen programmer C .. "tapi bagaimana dengan waktu kompilasi yang meningkat untuk menggunakan templat ??" Apakah ini benar-benar layak untuk kasus ini?
bobobobo

@obobobo Saya tidak pernah punya masalah dengan waktu kompilasi saya: P, tapi saya tidak pernah bekerja pada proyek yang waktu kompilasi akan menjadi masalah. Orang bisa berpendapat bahwa waktu kompilasi membenarkan tidak menggunakan mengapung ketika Anda membutuhkan bilangan bulat.
Luke B.

@obobobo Dengan instantiations eksplisit dan tidak termasuk file inline Anda di header, waktu kompilasi tidak akan berbeda. Selain itu, template sudut braket mengasapi hanyalah salah satu typedefpergi.
Samaursa

7

Terlepas dari kecepatan, pertanyaan pertama yang harus Anda bertanya pada diri sendiri ketika melakukan setiap warisan adalah jika Anda akan menggunakan mereka polymorphically. Lebih khusus lagi, apakah ada situasi di mana Anda dapat melihat diri Anda menggunakan Vector3seolah-olah itu Vector2(yang, dengan mewarisi darinya, Anda secara eksplisit mengatakan bahwa Vector3 "is-a" Vector2).

Jika tidak, maka Anda tidak harus menggunakan warisan. Anda seharusnya tidak menggunakan warisan untuk membagikan kode. Itulah gunanya komponen dan fungsi eksternal, bukan berarti Anda akan membagikan kode apa pun di antara mereka.

Yang sedang berkata, Anda mungkin ingin cara mudah untuk mengubah Vector3 s ke Vector2s, dan dalam hal ini Anda dapat menulis operator yang berlebihan yang secara implisit akan memotong Vector3ke a Vector2. Tetapi Anda tidak seharusnya mewarisi.


Terima kasih, saya pikir itu menyoroti masalah, saya melihat ini dari sudut pandang "berbagi kode" (yaitu tidak harus "mengetik ulang" nilai-nilai X & Y).
Tandai Ingram

+1 jawaban yang bagus, tidak ada penggunaan polimorfik antara vektor dengan ukuran yang berbeda.
Luke B.

Ini adalah hal terbesar yang akan saya tambahkan ke jawaban saya sendiri - +1 pasti. (Meskipun ada keadaan aneh di mana saya bisa membayangkan menginginkan polimorfisme - misalnya, game 'heightmap' 2.5d di mana hal-hal seperti pemeriksaan jarak, lintasan dll akan secara kanonik ingin dilakukan dalam 2d tetapi Anda masih perlu memberikan koordinat 3d untuk objek)
Steven Stadnicki

@ LukasB. Meskipun dalam kasus OP saya setuju bahwa tampaknya tidak ada alasan untuk mewarisi dari Vector2tetapi mewarisi dari pangkalan Vector<N>? Itu masuk akal. Selain itu, mengapa warisan secara otomatis berarti perilaku polimorfik? Salah satu hal terbaik tentang C ++ adalah Anda dapat memiliki warisan biaya nol. Tidak perlu menambahkan setiap metode virtual (termasuk destructors virtual) di dasar Vector<N>kelas.
Samaursa

5

Tidak, karena setiap metode perlu ditimpa juga, Anda tidak akan menggunakan pewarisan darinya.

Jika ada sesuatu, mereka berdua bisa mengimplementasikan antarmuka vektor. Namun, karena Anda mungkin tidak ingin menambahkan / sub / dot / dst antara Vector2 dan Vector3 ini akan memiliki efek samping yang tidak diinginkan. Dan memiliki parameter yang berbeda dll akan merepotkan.
Jadi saya benar-benar tidak dapat melihat pro dari warisan / antarmuka dalam hal ini.

Contohnya adalah kerangka Libgdx, di mana Vector2 dan Vector3 tidak ada hubungannya satu sama lain, selain memiliki jenis metode yang sama.


2

Jika Anda berencana untuk menggunakan array SIMD kemungkinan yang terbaik. Jika Anda masih ingin menggunakan overloading operator, Anda dapat mempertimbangkan untuk menggunakan antarmuka / mixin untuk mengakses array yang mendasarinya - misalnya, di sini adalah titik awal yang hanya memiliki (belum teruji) Add.

Perhatikan bagaimana saya belum memberikan X/ Y/ Z, masing-masing VectorXkelas akan mewarisi langsung dari satu ini - untuk alasan yang sama yang ditentukan oleh orang lain. Namun, saya telah melihat array yang digunakan sebagai vektor berkali-kali di alam liar.

#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);
    }
};

Penafian: C ++ saya mungkin payah, sudah lama sejak saya menggunakannya.


Tunggu , apakah penggunaan _aligned_mallocberarti bug yang saya buka bukan bug?
bobobobo

Anda seharusnya tidak menggunakan gips pointer untuk memasukkan nilai Anda ke dalam __m128register, Anda harus menggunakannya _mm_loadu_ps. Kelas sampel yang baik ada di sini di bawah "vectorclass.zip"
bobobobo

@bobobobo Saya akan melakukan upaya terbaik untuk mengedit - perhatikan secara khusus penafian;).
Jonathan Dickinson

@obobobo _mm_loadu_psakan bekerja untuk Anda dengan struct itu (di mana _mm_load_pstidak akan). Saya juga menambahkan saran Anda - silakan mengedit pertanyaan jika Anda merasa saya menggonggong pohon yang salah (sudah lama sejak saya menggunakan C [++]).
Jonathan Dickinson

1

Lain con serius untuk memiliki Vec3 mewarisi dari Vec2 atau, bisa dibilang, untuk memiliki kedua mewarisi dari kelas Vector tunggal: kode Anda akan melakukan banyakoperasi pada vektor, sering dalam situasi kritis waktu, dan sangat penting bagi Anda untuk memastikan bahwa semua operasi itu secepat mungkin - jauh lebih banyak daripada banyak objek lain yang tidak cukup universal atau level rendah. Walaupun kompiler yang baik akan melakukan yang terbaik untuk meratakan overhead warisan, Anda masih lebih mengandalkan kompiler di sana daripada yang Anda inginkan; sebagai gantinya, saya akan membangun mereka sebagai struct dengan overhead sesedikit mungkin dan bahkan mungkin mencoba dan membuat sebagian besar fungsi yang menggunakannya (dengan pengecualian hal-hal seperti operator + yang tidak dapat membantu) menjadi global daripada metode pada struct. Optimalisasi awal umumnya direkomendasikan, dan dengan alasan yang sangat baik,


1
-1 karena: kelas dan struct hanya memiliki implikasi hibah akses dalam C ++ dan OP tidak menentukan bahasa bagaimanapun, fungsi anggota non-virtual memiliki implikasi kinerja yang sama dengan fungsi non-anggota, dan fungsi anggota virtual (yang berpotensi menunjukkan masalah yang Anda khawatirkan) hanya ada jika Anda membuatnya, tidak hanya dengan memanfaatkan warisan.

2
@JoshPetrie Poin valid di semua lini; Saya cenderung 'default' ke C / C ++ dan melihat pertanyaan melalui lensa itu. Saya tidak percaya ada kinerja yang sah (serta konseptual) alasan untuk tidak pergi rute warisan, pikiran Anda, tapi aku bisa saja jauh lebih baik pada rincian spesifik. Saya akan mencoba dan meninjau kembali ini dan melihat apakah saya dapat memberikan akuntansi yang lebih baik.
Steven Stadnicki
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.