2015-07-07 11 views
7

यहाँ एक बहुत ही सरल वर्ग पदानुक्रम है:कहां चाहिए! = ऑपरेटर को कक्षा पदानुक्रम में परिभाषित किया जाना चाहिए?

class A 
{ 
public: 
    A(int _a) : a(_a) {} 

    virtual bool operator==(const A& right) const 
    { 
     return a == right.a; 
    } 

    virtual bool operator!=(const A& right) const 
    { 
     return !(*this == right); 
    } 

    int a; 
}; 

class B : public A 
{ 
public: 
    B(int _a, int _b) : A(_a), b(_b) {} 

    virtual bool operator==(const B& right) const 
    { 
     return A::operator==(right) && b == right.b; 
    } 

    int b; 
}; 

आप देख सकते हैं, ऑपरेटर = आधार वर्ग में परिभाषित किया गया है। क्योंकि मैं बहुत आलसी हूं, मैं सभी व्युत्पन्न कक्षाओं में ऐसे सरल कोड को डुप्लिकेट नहीं करना चाहता हूं।

दुर्भाग्यवश, इस कोड के साथ:

A a4(4), a5(5), a4bis(4); 
assert(a4 == a4bis); 
assert(a4 != a5); 

B b1(4,5), b2(4,6); 
assert(!(b1 == b2)); 
assert(b1 != b2); // fails because B::operator== is not called! 

b1 != b2 रिटर्न झूठी है, क्योंकि यह A::operator!= निष्पादित करता है जो कहता है तो A::operator== बल्कि B::operator== से (भले ही ऑपरेटर आभासी है, के रूप में व्युत्पन्न वर्ग संस्करण पैरामीटर अलग है, वे कर रहे हैं vtable में लिंक नहीं है)।

तो कक्षा पदानुक्रम के लिए सामान्य तरीके से एड्रेस! = ऑपरेटर का सबसे अच्छा तरीका क्या है?

एक समाधान प्रत्येक वर्ग में यह दोहराने के लिए है, B तो होगा:

virtual bool operator!=(const B& right) const 
{ 
    return !(*this == right); 
} 

लेकिन यह एक दर्द आप कई श्रेणियां होती हैं जब है .... मैं 30 ....

एक अन्य समाधान एक सामान्य टेम्पलेट दृष्टिकोण है करने के लिए होगा:

template <class T> 
bool operator!=(const T& left, const T& right) 
{ 
    return !(left == right); 
} 

लेकिन यह किसी भी != ऑपरेटर किसी भी वर्ग द्वारा परिभाषित किया गया नजरअंदाज .... तो यह अगर एक decla जोखिम भरा हो सकता है इसे अलग-अलग लाल करें (या अगर किसी ने == को != पर कॉल किया है, तो यह अनंत लूप के साथ समाप्त होगा ...)। तो मुझे लगता है कि यह समाधान बहुत असुरक्षित है .... सिवाय इसके कि हम अपने पदानुक्रम के शीर्ष-स्तरीय वर्ग (A) से प्राप्त सभी वर्गों के लिए टेम्पलेट को सीमित करने के लिए सीमित कर सकते हैं .... लेकिन मैं नहीं करता लगता है कि यह बिल्कुल करने योग्य है।

नोट: मैं अभी तक सी ++ 11 का उपयोग नहीं कर रहा हूं ... इसके बारे में खेद है।

+0

इसके अलावा, वर्तमान में, 'ए (42) == बी (42, 0)' जैसा कि आप केवल 'ए' भाग की तुलना करते हैं ... – Jarod42

+0

स्पष्टता के लिए और यह सुनिश्चित करने के लिए कि ए स्वतंत्र रूप से काम करता रहे (यदि आप ' टी इसे चाहते हैं, आपको इससे बी प्राप्त करने की आवश्यकता नहीं है), लागू करें! = ए, बी, सी, डी और जो भी आपके पदानुक्रम में है। दोबारा अगर आपको उनकी ज़रूरत नहीं है, तो आपको बिल्कुल हासिल करने की आवश्यकता क्यों है? – Robinson

उत्तर

4

इस तरह कुछ कैसे?

class A { 
    protected : 
    virtual bool equals(const A& right) const { 
     return (a == right.a); 
    } 

    public : 
    A(int _a) : a(_a) { } 

    bool operator==(const A& right) const { 
     return this->equals(right); 
    } 
    bool operator!=(const A& right) const { 
     return !(this->equals(right)); 
    } 

    int a; 
}; 

class B : public A { 
    protected : 
    virtual bool equals(const A& right) const { 
     if (const B* bp = dynamic_cast<const B*>(&right)) { 
     return A::equals(right) && (b == bp->b); 
     } 
     return false; 
    } 

    public : 
    B(int _a, int _b) : A(_a), b(_b) { } 

    int b; 
}; 

एक अलग (आभासी) समारोह equals की तुलना तर्क ले जाएँ, और operator== और operator!= आधार वर्ग में परिभाषित से कि फ़ंक्शन को कॉल करें।

ऑपरेटरों को व्युत्पन्न कक्षाओं में फिर से परिभाषित करने की आवश्यकता नहीं है। व्युत्पन्न कक्षा में तुलना बदलने के लिए, बस equals ओवरराइड करें।

ध्यान दें कि उपरोक्त कोड में dynamic_cast का उपयोग यह सुनिश्चित करने के लिए किया जाता है कि रनटाइम प्रकार तुलना करने के लिए एक वैध प्रकार है। अर्थात। B::equals के लिए, यह सुनिश्चित करने के लिए प्रयोग किया जाता है कि right एक B है - यह आवश्यक है क्योंकि अन्यथा right में b सदस्य नहीं होगा।

+2

इसके साथ आपके पास 'बी (42, 0) है! = ए (42)' लेकिन फिर भी 'ए (42) == बी (42, 0) '। इसे एकाधिक प्रेषण की आवश्यकता होगी। – Jarod42

+0

क्या आप अपने दृष्टिकोण के बारे में कुछ और शब्द कह सकते हैं? धन्यवाद :-) – Wolf

+0

@ जारोड 42: यह एक विकल्प है जिसे आप बनाते हैं (और मैंने ओपी से व्यवहार की प्रतिलिपि बनाई है)। यदि आप समानता ऑपरेटरों को कम्यूटिव करना चाहते हैं, तो डबल प्रेषण (जैसा कि आप सुझाव देते हैं) वास्तव में मदद कर सकते हैं। –

5

B में आपका समारोह ...

virtual bool operator==(const B& right) const 

... नहीं ओवरराइड A में समारोह ...

virtual bool operator==(const A& right) const 

करता है ... क्योंकि तर्क प्रकार भिन्न होते हैं। (मतभेद केवल covariant वापसी प्रकार के लिए अनुमति दी जाती है।)

आप इसे सही हैं, तो आप फिर अन्य A को B वस्तुओं और A व्युत्पन्न वस्तुओं, जैसे तुलना करने के लिए चयन करने में सक्षम हो जाएगा शायद: उपरोक्त typeid तुलना का उपयोग कर का मतलब है

bool operator==(const A& right) const override 
{ 
    if (A::operator==(right)) 
     if (typeid(*this) == typeid(right)) 
      return b == static_cast<const B&>(right).b; 
    return false; 
} 

ध्यान दें कि केवल B वस्तुओं बराबर की तुलना करेंगे: किसी भी B किसी भी B व्युत्पन्न वस्तु को असमान की तुलना करेंगे। यह हो सकता है कि आप जो चाहें हो या न हो।

B::operator== के कार्यान्वयन के साथ, मौजूदा != कार्यान्वयन operator== उचित रूप से लपेट जाएगा।Jarod42 का मानना ​​है के रूप में, अपने A::operator== कि में मजबूत नहीं जब बाएं हाथ की ओर मूल्य एक A केवल A दाएँ हाथ की ओर वस्तु का टुकड़ा तुलना में किया जाएगा है ... आप पसंद कर सकते हैं:

virtual bool operator==(const A& right) const 
{ 
    return a == right.a && typeid(*this) == typeid(right); 
} 

यह वही समस्या है जो B::operator== उपरोक्त है: उदाहरण के लिए एक A एक व्युत्पन्न वस्तु के बराबर तुलना करेगा जो आगे डेटा सदस्यों को पेश नहीं करता था।

+0

क्या वे गतिशील_कास्ट और टाइपिड वास्तव में सुरक्षित हैं (यहां तक ​​कि टेम्पलेट कक्षाओं का उपयोग करते समय भी, क्योंकि मेरी कक्षा किराया टेम्पलेट्स है ....)? – jpo38

+0

@ jpo38: वे सुरक्षित हैं, हां ... प्रत्येक टेम्पलेट तत्कालता के लिए अलग आरटीटीआई/टाइपइन्फो ऑब्जेक्ट बनाए जाएंगे। –

+0

धन्यवाद। आपका कोड welI काम करता है, लेकिन मैं 'सैंडर डी डाइकर' समाधान पसंद करता हूं क्योंकि मैं व्युत्पन्न क्लास (बी) को ए ऑब्जेक्ट को बी के रूप में डालने के बजाय माता-पिता वर्ग (ए) को कास्टिंग करने के लिए कास्ट करने के बजाय पसंद करता हूं (यह हो सकता है कक्षा पदानुक्रम विशाल होने पर पढ़ने के लिए एक कोड को मुश्किल हो जाता है)। – jpo38

संबंधित मुद्दे