क्या वेक्टर 3 को वेक्टर 2 से विरासत में मिलना चाहिए?


18

मैं वर्गों की एक जोड़ी बना रहा हूं Vector2(एक्स और वाई) और Vector3(एक्स, वाई और जेड), लेकिन मैं बनाने के लिए है कि क्या पता नहीं है Vector3से विरासत Vector2, या फिर से लागू सदस्य चर के लिए कि क्या m_xऔर m_yफिर? प्रत्येक पक्ष के पक्ष और विपक्ष क्या हैं (वंशानुक्रम बनाम पुनर्वितरण)।

संपादित करें: मैं C ++ (VS2010) का उपयोग कर रहा हूं।


1
क्यों न आयामी वैक्टर के लिए एक सामान्य वेक्टर वर्ग लिखें और फिर (यदि आवश्यक हो) एक वेक्टर 2 और एक वेक्टर 3 वर्ग विरासत में मिला। तुम भी सामान्य वर्ग और पूर्णांक वैक्टर और फ्लोट वैक्टर के लिए इनहेरिट संस्करणों के लिए टेम्पलेट्स का उपयोग कर सकते हैं। संपादित करें: आप एक अनुकूलित गणित पुस्तकालय का उपयोग क्यों नहीं करते हैं?
दानिशार १६'१२ को २१:२२

5
कल्पना "Vector3 एक Vector2 है" की कोई खिंचाव से, वे सकता है एक माता पिता VectorN से दोनों इनहेरिट हालांकि
विम

1
हाँ, लेकिन तब आपको शायद एक वर्चुअल टेबल की आवश्यकता होगी और यह उन मामलों में से एक है जहां रनटाइम और मेमोरी की लागत मायने रख सकती है। आदर्श रूप में, जहां तक ​​स्मृति का संबंध है , Vector3सिर्फ 3 होना चाहिए floats। यह कहना असंभव नहीं है, बस मैंने कभी ऐसा नहीं देखा है कि एक उत्पादन इंजन में।
लॉरेंट कौविदो

2
हां, मुझे ऐसा लगता है। जब तक आपको किसी और चीज की जरूरत नहीं है floats। तुम्हें पता है, YAGNI, KISS, वह सब सामान। Vector2, Vector3और Vector4कोई विरासत के साथ और floatsकेवल वास्तव में खेल इंजन में वास्तविक मानक है।
लॉरेंट कौविदो

1
मुझे आशा है कि आपका मतलब था typedef float real;;)।
मार्क इंग्राम

जवाबों:


47

नहीं यह नहीं होना चाहिए। केवल एक चीज जो आप विरासत से उपयोग कर रहे हैं वह है xऔर yघटक। Vector2कक्षा में उपयोग की जाने वाली विधियाँ एक कक्षा में उपयोगी नहीं होंगी Vector3, वे अलग-अलग तर्क ले सकती हैं और विभिन्न प्रकार के सदस्य चर पर कार्रवाई कर सकती हैं।


+1, मुझे पॉपअप पर अधिक ध्यान देना चाहिए ताकि मैं अनावश्यक सामान न लिखूं।
मत्स्येमन

8
शास्त्रीय विरासत का अति प्रयोग । एक Vector3IS-NOT-A Vector2(इसलिए यह विरासत में नहीं होना चाहिए), लेकिन एक AppleIS-A Fruit(इसलिए यह विरासत में मिल सकता है)। आप अपने मन काफी मोड़ हैं, तो एक Vector3HAS-ए Vector2उस में है, लेकिन प्रदर्शन हानि और कठिनाई कोडिंग साधन आप के लिए पूरी तरह से अलग वर्ग लिखेंगे Vector3और Vector2
बॉबोबोबो

लेकिन आप (मेरी राय में) को 2 डी वेक्टर और उससे 3 डी वेक्टर विरासत में पाने के लिए एक एन-डायमेंशनल वेक्टर क्लास लिखना चाहिए।
डनिजर ११'१२ को १२:२१

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में थोड़ा और अधिक ब्लोटी होगाVector3<float> v
bobobobo

@ बोबोबोबो हां, मैं सहमत हूं। मेरी वेक्टर कक्षाएं आमतौर पर बिना माता-पिता के साथ vec2 और vec3 हैं, लेकिन फिर भी उन्हें टेम्पलेट बनाते हैं। यदि आप वेक्टर 3 <फ्लोट> लिख रहे हैं, तो आप हमेशा इसे टाइप कर सकते हैं
ल्यूक बी।

..और अब C प्रोग्रामर का तर्क .. "लेकिन टेम्पलेट का उपयोग करने के लिए बढ़े हुए संकलन समय के बारे में क्या ??" क्या इस मामले में वास्तव में इसके लायक है?
बॉबोबोबो

@ बोबोबोबो को कभी भी मेरे संकलन के समय कोई समस्या नहीं हुई: पी, लेकिन मैंने कभी भी इस परियोजना पर काम नहीं किया कि संकलन समय एक मुद्दा होगा। कोई यह तर्क दे सकता है कि जब आप पूर्णांकों की आवश्यकता होती है तो संकलन समय फ़्लोट्स का उपयोग करने का औचित्य नहीं रखता है।
ल्यूक बी।

@bobobobo स्पष्ट तात्कालिकता के साथ और हेडर में आपकी इनलाइन फ़ाइल को शामिल नहीं करता है, संकलन समय अलग नहीं होगा। इसके अतिरिक्त, टेम्पलेट कोण ब्रैकेट ब्लोट केवल एक typedefदूर है।
समरसता

7

गति के बावजूद, किसी भी विरासत को करते समय पहला सवाल आपको खुद से पूछना चाहिए कि क्या आप पॉलिमॉर्फिक रूप से उनका उपयोग करने जा रहे हैं। अधिक विशेष रूप से, क्या कोई ऐसी स्थिति है जहां आप अपने आप को एक का उपयोग करके देख सकते हैं Vector3जैसे कि यह एक था Vector2(जो इसे से विरासत में मिला है, आप स्पष्ट रूप से कह रहे हैं कि एक वेक्टर 3 "-एक" वेक्टर 2 है)।

यदि नहीं, तो आपको विरासत का उपयोग नहीं करना चाहिए। आपको कोड साझा करने के लिए विरासत का उपयोग नहीं करना चाहिए। यही कारण है कि घटकों और बाहरी कार्यों के लिए हैं, न कि आप उन दोनों के बीच किसी भी तरह कोड साझा कर रहे हैं।

यह कहा जा रहा है, आप चाहते हैं कि आप एस से एस में परिवर्तित करने के लिए आसान तरीके चाहते हैं , और उस स्थिति में आप एक ऑपरेटर अधिभार लिख सकते हैं जो कि ए को संक्षेप में छोटा कर देगा । लेकिन आपको विरासत में नहीं मिलना चाहिए। Vector3Vector2Vector3Vector2


धन्यवाद, मुझे लगता है कि इस समस्या पर प्रकाश डाला गया है, मैं इसे "कोड साझा करने" के दृष्टिकोण से देख रहा था (यानी एक्स और वाई मूल्यों को "पुनः टाइप" करने के लिए नहीं)।
मार्क इंग्राम

+1 महान उत्तर, विभिन्न आकारों के वैक्टर के बीच कोई बहुरूपी उपयोग नहीं है।
ल्यूक बी।

यह मेरे लिए अपने स्वयं के उत्तर - +1 को निश्चित रूप से जोड़ने वाली सबसे बड़ी बात है। (हालांकि अजीब परिस्थितियां हैं जिनमें मैं बहुरूपता चाहने की कल्पना कर सकता हूं - उदाहरण के लिए, 2.5 डी 'हाइटमैप' गेम, जहां दूरी की जांच, पथिंग आदि जैसी चीजें कैनोनिकली 2 डी में की जानी चाहिए, लेकिन आपको अभी भी वस्तुओं के लिए 3 डी निर्देशांक प्रदान करने की आवश्यकता है)
स्टीवन स्टडनिक

@LukeB। हालांकि ओपी के मामले में मैं सहमत हूं कि ऐसा लगता है कि Vector2आधार से विरासत में लेने का कोई कारण नहीं है Vector<N>? यह सही समझ में आता है। इसके अलावा, वंशानुक्रम का तात्पर्य बहुपद व्यवहार से क्यों है? C ++ के बारे में सबसे अच्छी चीजों में से एक यह है कि आपके पास शून्य लागत विरासत हो सकती है। आधार वर्ग में किसी भी वर्चुअल तरीके (वर्चुअल डिस्ट्रक्टर्स सहित) को जोड़ने की आवश्यकता नहीं है Vector<N>
समोरसा

5

नहीं, चूंकि प्रत्येक विधि को ओवरराइड करने की आवश्यकता होगी और साथ ही आपको वास्तव में इससे विरासत में प्राप्त करने का कोई फायदा नहीं होगा।

यदि कुछ है, तो वे दोनों एक वेक्टर इंटरफ़ेस लागू कर सकते हैं। हालाँकि, चूंकि आप शायद वेक्टर 2 और वेक्टर 3 के बीच / उप / डॉट / डीएसटी जोड़ना नहीं चाहते हैं, इससे अवांछित दुष्प्रभाव होंगे। और विभिन्न मापदंडों आदि का होना एक परेशानी होगी।
इसलिए मैं वास्तव में इस मामले में इनहेरिटेंस / इंटरफ़ेस के किसी भी नियम को नहीं देख सकता।

एक उदाहरण Libgdx ढांचे, जहां है Vector2 और Vector3 , अन्य तरीकों के एक ही प्रकार की तुलना में एक दूसरे के साथ कोई लेना देना नहीं है।


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

डिस्क्लेमर: मेरा सी ++ बेकार हो सकता है, जब से मैंने इसे इस्तेमाल किया है तब से यह एक समय है।


रुको यार , क्या आपके द्वारा खोले_aligned_malloc गए बग का उपयोग वास्तव में बग नहीं है?
बोब्बोबो

आपको __m128रजिस्टर में अपने मूल्यों को प्राप्त करने के लिए सूचक का उपयोग नहीं करना चाहिए , आपको _mm_loadu_psइसके बजाय उपयोग करना चाहिए । एक अच्छा नमूना वर्ग यहाँ "वेक्टरक्लास.ज़िप" के तहत है
बॉबोबोबो

@bobobobo मैं एक संपादन में सबसे अच्छा प्रयास करूँगा - अस्वीकरण के लिए विशेष ध्यान रखें;)।
जोनाथन डिकिंसन

@bobobobo _mm_loadu_psको आपके लिए उस संरचना (जहां _mm_load_psनहीं होगा) के साथ काम करना चाहिए । मैंने आपके सुझाव को भी जोड़ा - सवाल को संपादित करने के लिए स्वतंत्र महसूस करें यदि आपको लगता है कि मैं गलत पेड़ को भौंक रहा हूं (सी [++] का उपयोग करने के बाद से मुझे थोड़ी देर हो गई है)।
जोनाथन डिकिंसन

1

Vec2 से Vec3 वारिस होने का एक और गंभीर मामला या, यकीनन, दोनों एक ही वेक्टर वर्ग से विरासत में मिला: आपका कोड बहुत कुछ कर रहा होगावैक्टर पर संचालन, अक्सर समय-महत्वपूर्ण स्थितियों में, और यह सुनिश्चित करने के लिए आपके सर्वोत्तम हित में बहुत अधिक है कि उन सभी कार्यों को जितनी तेजी से किया जा सकता है - उससे कहीं अधिक यह उन कई अन्य वस्तुओं के लिए है जो नहीं हैं इतना सार्वभौमिक या निम्न-स्तर। जबकि एक अच्छा संकलक किसी भी विरासत उपरि को समतल करने के लिए अपनी पूरी कोशिश करेगा, आप अभी भी संकलक पर अधिक भरोसा कर रहे हैं, जहां आप करना चाहते हैं; इसके बजाय, मैं उन्हें यथासंभव ओवरहेड के साथ स्ट्रक्चर्स के रूप में बनाऊंगा और संभवत: कोशिश भी करूंगा और उनमें से अधिकांश कार्यों का उपयोग करूंगा (ऑपरेटर की तरह चीजों के अपवाद के साथ + जो वास्तव में मदद नहीं कर सकते हैं) के बजाय तरीकों पर ग्लोबल्स हों। struct। आमतौर पर प्रारंभिक अनुकूलन की सिफारिश की जाती है, और उत्कृष्ट कारण के साथ,


1
-1 क्योंकि: वर्ग और संरचना में केवल C ++ में एक्सेस अनुदान निहितार्थ होते हैं और ओपी ने किसी भी तरह से एक भाषा निर्दिष्ट नहीं की है, गैर-आभासी सदस्य फ़ंक्शंस में गैर-सदस्य फ़ंक्शंस और वर्चुअल सदस्य फ़ंक्शंस के रूप में समान प्रदर्शन निहितार्थ हैं (जो संभावित प्रदर्शन करते हैं आप जिन मुद्दों के बारे में चिंतित हैं) केवल मौजूद हैं यदि आप उन्हें बनाते हैं, न कि केवल विरासत का उपयोग करके।

2
@JoshPetrie सभी मोर्चों पर मान्य अंक; मैं C / C ++ को 'डिफ़ॉल्ट' करता हूं और इसलिए उस लेंस के माध्यम से प्रश्न देखा। मेरा मानना है कि उत्तराधिकार के मार्ग पर न जा पाने के लिए वैध प्रदर्शन (साथ ही वैचारिक) कारण हैं, आप पर ध्यान दें, लेकिन मैं विशिष्ट विवरणों पर बहुत बेहतर हो सकता था। मैं यह कोशिश करूँगा और यह देखूंगा कि क्या मैं बेहतर हिसाब दे सकता हूँ।
स्टीवन स्टैडनिक
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.