Vector3 Vector2'den mi mi almalı?


18

Ben sınıfların bir çift yaratıyorum Vector2(X Y) ve Vector3(X, Y ve Z) ama yapmak olmadığını bilmiyorsanız Vector3gelen devralır Vector2, yoksa tekrar uygulamasını üye değişkenlere m_xve m_ytekrar? Her bir tarafın artıları ve eksileri nelerdir (kalıtım vs yeniden tanımlama).

Düzenleme: C ++ (VS2010) kullanıyorum.


1
Neden n boyutlu vektörler için genel bir vektör sınıfı yazmıyorsunuz ve sonra (gerekirse) bir vector2 ve bir vector3 sınıfını miras alıyorlar. Ayrıca genel sınıf için şablonlar kullanabilir ve tamsayı vektörleri ve kayan noktalı vektörler için miras sürümleri de kullanabilirsiniz. Düzenleme: Neden optimize edilmiş bir matematik kütüphanesi kullanmıyorsunuz?
danijar

5
"Vector3 bir Vector2" hayal gücünün gerilmesiyle, her ikisi de bir VectorN ebeveynden miras alabilirler
wim

1
Evet, ama muhtemelen sanal bir masaya ihtiyacınız olacak ve bu da çalışma zamanı ve bellek maliyetlerinin önemli olabileceği durumlardan biri. İdeal olarak, hafıza ile ilgili olarak a Vector3sadece 3 olmalıdır floats. Bunun imkansız olduğunu söylememek, sadece bunu bir üretim motorunda görmedim.
Laurent Couvidou

2
Evet, öyle düşünüyorum. Başka bir şeye ihtiyacınız olmadığı sürece floats. Bilirsin, YAGNI, KISS, tüm bu şeyler. Vector2, Vector3Ve Vector4hiçbir miras ile ve floatssadece gerçekten oyun motorlarında fiili standarttır.
Laurent Couvidou

1
Umarım demek istedin typedef float real;;).
Mark Ingram

Yanıtlar:


47

Hayır olmamalı. Kalıtımdan kullanacağınız tek şey xve ybileşenleridir. Bir Vector2sınıfta kullanılan yöntemler bir sınıfta yararlı Vector3olmaz, muhtemelen farklı argümanlar alır ve farklı sayıda üye değişken üzerinde işlem yapar.


+1, pop-up'a daha fazla dikkat etmeliyim, bu yüzden gereksiz şeyler yazmıyorum.
2012'de Matsemann

8
Klasik kalıtım aşırı kullanımı . Bir Vector3IS-NOT-A Vector2(miras kalmamalı), ama AppleIS-A Fruit(miras alabilir). Zihninizi yeterince Vector3bükerseniz , içindeki bir HAS-A Vector2, ancak performans kaybı ve zorluk kodlaması, Vector3ve için tamamen ayrı sınıflar yazacağınız anlamına gelir Vector2.
bobobobo

Ama 2d vektörü ve 3B vektörü miras almak için n-boyutlu bir vektör sınıfı yazabilirsiniz.
danijar

8

C ++ ile yapabileceğiniz ilginç bir şey var (Bir dil belirtmediniz ve bu cevap çoğunlukla alternatifleri görmenin güzel olduğunu düşünüyorum, ancak bunun çoğu durumda yararlı olduğuna inanmıyorum.)

Şablonları kullanarak şöyle bir şey yapabilirsiniz:

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

ve bu şu şekilde kullanılabilir:

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

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

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

Dediğim gibi, bunun yararlı olduğunu düşünmüyorum ve dot / normalize / diğer şeyleri uygulamaya çalıştığınızda ve herhangi bir sayıda vektörle jenerik olmaya çalıştığınızda muhtemelen hayatınızı zorlaştıracaktır.


Evet, tüm jeneriklik güzel görünüyor, sadece çoğu zaman sadece 3 kayan nokta bileşenine sahip standart bir 3 vektörüne ihtiyacınız var - tüm köşeli parantezler Vector3f vbiraz daha şişkin olacakVector3<float> v
bobobobo

@bobobobo Evet, katılıyorum. Vektör sınıflarım genellikle üst öğe olmadan vec2 ve vec3'tür, ancak yine de onları şablon yaparlar. Vector3 <float> yazmak sizi rahatsız ediyorsa, her zaman onu tanımlayabilirsiniz
Luke B.

..Ve şimdi C programcısının argümanı .. "Peki ya şablon kullanmak için daha fazla derleme zamanı ??" Bu durumda gerçekten buna değer mi?
15'te bobobobo

@bobobobo Derleme sürelerimde hiç sorun yaşamadım: P, ama derleme zamanının bir sorun olacağı bir projede hiç çalışmadım. Tamsayıya ihtiyacınız olduğunda derleme sürelerinin şamandıra kullanmamayı haklı çıkarabileceği söylenebilir.
Luke B.17

@bobobobo Açık örneklemelerle ve satır içi dosyanızı üstbilgiye dahil etmeden derleme süreleri farklı olmayacaktır. Ek olarak, şablon açılı ayraç bloat sadece bir typedefuzaklıktadır.
Samaursa

7

Hızdan bağımsız olarak, herhangi bir miras alırken kendinize sormanız gereken ilk soru , bunları polimorfik olarak kullanacağınızdır. Daha spesifik olarak, kendinizi kullanarak görebilirsiniz herhangi bir durum yoktur Vector3bir sanki Vector2(ondan devralan tarafından, açıkça bir vector3 "is-a" olduğunu Vector2 söylüyorsun olan).

Değilse, miras kullanmamalısınız. Kodu paylaşmak için miras kullanmamalısınız. Bileşenler ve harici işlevler bunun içindir, yine de aralarında herhangi bir kod paylaşacaksınız değil.

Varlık dedi ki, sen kolay yolu isteyebilirsiniz dönüştürme Vector3 için lar Vector2s ve bu durumda dolaylı keser bir operatör aşırı yazabilirsiniz Vector3a Vector2. Ama miras kalmamalısınız.


Teşekkürler, bu sorunu vurguladı düşünüyorum, ben bir "kod paylaşımı" bakış açısından bakıyordu (yani "X & Y değerleri yeniden yazmak zorunda değilsiniz).
Mark Ingram

+1 büyük cevap, farklı boyutlardaki vektörler arasında polimorfik kullanım yoktur.
Luke B.17

Bu, kendi cevabıma ekleyeceğim en büyük şeydi - kesin olarak +1. (Ben hangi garip durumlar olmasına rağmen edebilirsiniz isteyen biçimlilik hayal - mesafe çekleri gibi şeyler, vb yol oluşturma canonically 2d yapılır ama hala nesneler için 3d koordinatlarını sağlamanız gerekir edilecek isteyecektir örneğin 2.5d 'heightmap' oyunlar)
Steven Stadnicki

@LukeB. OP durumda olmasına rağmen ben ondan devralan için hiçbir neden yoktur görünüyor katılıyorum Vector2ama bir üs devralma Vector<N>? Bu çok mantıklı. Dahası, kalıtım neden otomatik olarak polimorfik davranış anlamına gelir? C ++ ile ilgili en iyi şeylerden biri sıfır maliyet mirasına sahip olabilmenizdir. Temel sınıfa herhangi bir sanal yöntem (sanal yıkıcılar dahil) eklemenize gerek yoktur Vector<N>.
Samaursa

5

Hayır, her yöntemin de geçersiz kılınması gerekeceğinden, aslında bu yöntemden miras almanın hiçbir faydası olmayacaktır.

Bir şey varsa, her ikisi de bir Vector arabirimi uygulayabilir. Bununla birlikte, bir Vector2 ve Vector3 arasına / sub / dot / dst eklemek istemediğiniz için bunun istenmeyen yan etkileri olacaktır. Ve farklı parametrelere vb. Sahip olmak bir güçlük olurdu.
Dolayısıyla bu durumda kalıtımın / arayüzün artılarını gerçekten göremiyorum.

Bir örnek, Vector2 ve Vector3'ün aynı tür yöntemlere sahip olmak dışında birbirleriyle hiçbir ilgisi olmayan Libgdx çerçevesidir .


2

SIMD dizileri kullanmayı planlıyorsanız muhtemelen en iyisidir. Hala operatör aşırı yüklemesini kullanmak istiyorsanız, temel diziye erişmek için bir arayüz / mixin kullanmayı düşünebilirsiniz - örneğin, burada sadece (denenmemiş) bir başlangıç ​​noktası vardır Add.

X/ Y/ Nasıl sağlamadığımı fark et Z, her VectorXsınıf doğrudan bu sınıftan miras alırdı - diğer insanlar tarafından belirtilen nedenlerle. Yine de, vahşi doğada birçok kez vektör olarak kullanılan diziler gördüm.

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

Yasal Uyarı: Benim C ++ berbat olabilir, kullandığımdan beri bir süre oldu.


Bekle adamım , açtığım böceğin kullanımının gerçekten bir hata olmadığı _aligned_mallocanlamına mı geliyor?
bobobobo

Değerlerinizi __m128kayıt defterine almak için işaretçi dökümleri kullanmamalısınız, _mm_loadu_psbunun yerine kullanmalısınız . İyi bir örnek sınıftır burada "vectorclass.zip" altında
bobobobo

@bobobobo Bir düzenlemede en iyi girişimde bulunacağım - yasal uyarıyı dikkate alın;).
Jonathan Dickinson

@bobobobo _mm_loadu_psbu yapı ile sizin için çalışmalıdır (nerede _mm_load_psolmaz). Ayrıca önerinizi ekledim - yanlış ağacı havlıyor olduğumu düşünüyorsanız soruyu düzenlemekten çekinmeyin (C [++] kullandığımdan beri bir süredir).
Jonathan Dickinson

1

Vec3'ü Vec2'den miras almanın veya tartışmalı olarak her ikisini de tek bir Vector sınıfından miras almanın bir başka ciddi koşulu: kodunuz çok şey yapacakvektörlerdeki işlemlerin, çoğu zaman zaman kritik durumlarda ve tüm bu işlemlerin olabildiğince hızlı olmasını sağlamak sizin yararınıza olacaktır - diğer birçok nesne için olduğundan çok daha fazla oldukça evrensel veya düşük seviyeli. İyi bir derleyici, herhangi bir miras yükünü düzleştirmek için elinden geleni yapsa da, hala derleyiciye istediğinizden daha fazla güveniyorsunuz; bunun yerine, onları olabildiğince az ek yüke sahip yapılar olarak inşa ederdim ve hatta bunları kullanan işlevlerin çoğunu dener ve yaparlar (operatör + gibi gerçekten yardım edilemeyen şeyler hariç) struct. Erken optimizasyon genellikle karşı ve mükemmel bir sebeple tavsiye edilir


1
-1 çünkü: sınıf ve yapı sadece C ++ 'da erişim hibe etkilerine sahiptir ve OP herhangi bir şekilde bir dil belirtmemiştir, sanal olmayan üye işlevleri üye olmayan işlevlerle aynı performans etkilerine ve sanal üye işlevlere (potansiyel olarak endişelendiğiniz konular) yalnızca miras kullanarak değil, yalnızca bunları yaparsanız mevcuttur.

2
@JoshPetrie Tüm cephelerde geçerli noktalar; C / C ++ 'varsayılan' eğilimindedir ve bu yüzden bu lens aracılığıyla soruyu gördüm. Ben do meşru performansı (yanı sıra kavramsal) sakıncası, kalıtım rotayı gitmiyor nedenlerini olduğuna inanıyorum, ama çok daha iyi özel ayrıntılar üzerinde olabilirdi. Bunu tekrar deneyeceğim ve daha iyi bir muhasebe yapıp yapamayacağımı göreceğim.
Steven Stadnicki
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.