2010-10-07 18 views
15

में मुझे ऑपरेटर = की विरासत में परेशानी हो रही है। यह कोड क्यों काम नहीं करता है, और इसे ठीक करने का सबसे अच्छा तरीका क्या है?ऑपरेटर की विरासत के साथ समस्या = सी ++

#include <iostream> 

class A 
{ 
public: 
    A & operator=(const A & a) 
    { 
     x = a.x; 
     return *this; 
    } 

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

    virtual int get() = 0; // Abstract 

protected: 
    int x; 
}; 

class B : public A 
{ 
public: 
    B(int x) 
    { 
     this->x = x; 
    } 

    int get() 
    { 
     return x; 
    } 
}; 

class C : public A 
{ 
public: 
    C(int x) 
    { 
     this->x = x; 
    } 

    int get() 
    { 
     return x; 
    } 
}; 

int main() 
{ 
    B b(3); 
    C c(7); 
    printf("B: %d C: %d B==C: %d\n", b.get(), c.get(), b==c); 

    b = c; // compile error 
    // error: no match for 'operator= in 'b = c' 
    // note: candidates are B& B::operator=(const B&) 

    printf("B: %d C: %d B==C: %d\n", b.get(), c.get(), b==c); 
    return 0; 
} 

उत्तर

30

एक उपयुक्त असाइनमेंट ऑपरेटर का समर्थन करना चाहिए को सौंपा जा , कंपाइलर आपके लिए एक घोषित करेगा। निहित रूप से घोषित प्रति-असाइनमेंट ऑपरेटर किसी भी विरासत असाइनमेंट ऑपरेटर (सी ++ में "नाम छिपाने" के बारे में पढ़ें) का अर्थ है, जिसका अर्थ है कि कोई विरासत असाइनमेंट ऑपरेटर अयोग्य नाम लुकअप प्रक्रिया (जो होता है) के लिए "अदृश्य" बन जाएगा जब आप b = c करते हैं), जब तक कि आप उन्हें "बिना छेड़छाड़" करने के लिए विशिष्ट कदम उठाते हैं।

आपके मामले में, कक्षा B में कोई स्पष्ट रूप से घोषित प्रति-कार्य ऑपरेटर नहीं है।जिसका मतलब है कि संकलक

B& B::operator =(const B&) 

स्पष्ट रूप से घोषित करेगा। यह A से प्राप्त ऑपरेटर को छुपाएगा। लाइन

b = c; 

संकलन नहीं करता, क्योंकि, यहां केवल उम्मीदवार ऊपर परोक्ष घोषित B::operator = (संकलक पहले से ही उस बारे में बताया) है; अन्य सभी उम्मीदवार छुपाए गए हैं। और चूंकि c परिवर्तनीय नहीं है B&, उपरोक्त असाइनमेंट संकलित नहीं करता है।

आप अपने कोड संकलन करना चाहते हैं, तो आप का उपयोग कर-घोषणा विरासत में मिला A::operator = को सामने लाने का वर्ग B की परिभाषा के लिए

using A::operator =; 

जोड़कर उपयोग कर सकते हैं। कोड अब संकलित होगा, हालांकि यह एक अच्छी शैली नहीं होगी। आपको यह ध्यान रखना होगा कि इस मामले में b = c असाइनमेंट A::operator = का आह्वान करेगा, जो शामिल वस्तुओं के केवल A भाग निर्दिष्ट करता है। इस तरह के मामलों में (लेकिन जाहिरा तौर पर है कि अपने इरादे है।)

वैकल्पिक रूप से, आप हमेशा एक योग्य नाम

b.A::operator =(c); 
+0

जब आप कहते हैं कि यह इसे छिपाएगा, तो मुझे पूरा यकीन है कि डिफ़ॉल्ट असाइनमेंट ऑपरेटर उपयोगकर्ता अधिभार का उपयोग करके "ए" भाग असाइन करेगा। हालांकि यह आपको ए से प्राप्त कुछ भी असाइन करने की अनुमति नहीं देता है, यही कारण है कि ओपी का कोड संकलित करने में विफल रहता है। – CashCow

+0

@CashCow: हाँ, आप सही हैं। इसे रखने का सही तरीका यह है कि विरासत 'ऑपरेटर =' का नाम * अयोग्य नाम लुकअप * के लिए अदृश्य हो जाता है। संकलक, ज़ाहिर है, अभी भी उस ऑपरेटर के बारे में जानता है और अभी भी इसे अन्य संदर्भों में उपयोग करता है। – AnT

3

बात ये है कि डिफ़ॉल्ट operator = कि संकलक किसी भी वर्ग है कि एक नहीं है के लिए उत्पन्न करता है आधार वर्ग 'operator = छिपा है है। इस विशेष मामले में, कंपाइलर दृश्यों के पीछे आपके लिए const B &B::operator =(const B &) उत्पन्न कर रहा है। आपका असाइनमेंट इस ऑपरेटर से मेल खाता है और class A में आपके द्वारा घोषित किए गए एक को पूरी तरह से अनदेखा करता है। चूंकि C& को B& में परिवर्तित नहीं किया जा सकता है, इसलिए संकलक आपके द्वारा देखी गई त्रुटि उत्पन्न करता है।

आप यह होना चाहते हैं, भले ही यह अभी भी परेशान हो। यह आपके द्वारा काम करने से लिखे गए कोड को रोकता है। आप इस तरह के कोड को काम नहीं करना चाहते हैं क्योंकि यह असंबद्ध प्रकारों की अनुमति देता है (बी और सी के पास एक आम पूर्वज है, लेकिन विरासत में एकमात्र महत्वपूर्ण रिश्ते माता-पिता हैं-> बच्चे-> पोते के रिश्ते, रिश्तों को झुकाव नहीं) एक को सौंपा जा सकता है एक और।

इसके बारे में एक आईएसए परिप्रेक्ष्य से सोचें। Car को Boat पर असाइन करने की अनुमति दी जानी चाहिए क्योंकि वे Vehicles दोनों हैं?

इस काम की तरह कुछ बनाने के लिए आपको Envelope/Letter पैटर्न का उपयोग करना चाहिए। लिफाफा (उर्फ हैंडल) एक विशेष श्रेणी है जो केवल नौकरी है, यह किसी विशेष आधार वर्ग (पत्र) से प्राप्त कुछ वर्ग का उदाहरण आयोजित करना है। हैंडल सभी परिचालनों को आगे बढ़ाता है लेकिन निहित वस्तु को असाइनमेंट करता है। असाइनमेंट के लिए यह ऑब्जेक्ट से असाइन की गई प्रतिलिपि की प्रतिलिपि (एक 'क्लोन' विधि (उर्फ आभासी कन्स्ट्रक्टर) का उपयोग करके आंतरिक ऑब्जेक्ट के उदाहरण को प्रतिस्थापित करता है।

+0

यह बिल्कुल नहीं एक से दूसरे व्युत्पन्न वर्ग से एक आधार subobject प्रतिलिपि बनाना चाहते हैं अनुचित है। एक उद्धरण से आदेश बनाने पर विचार करें। वे एक ही प्रकार के नहीं हैं, लेकिन वे ग्राहक (नाम/पता/आदि) के बारे में जानकारी का एक पूरा समूह साझा करते हैं और उत्पादों का आदेश देते हैं, फिर भी न तो दूसरे का सच्चा सुपरसैट है (आदेश में कोई उद्धरण समाप्ति तिथि नहीं है, उद्धरण संख्या नहीं है बिलिंग जानकारी)। बेशक, इस तरह का कार्य सामान्य कार्य होना चाहिए, न कि 'ऑपरेटर =', और उसके बाद व्युत्पन्न कक्षाओं से छिपकर कोई संकलक-उत्पन्न संस्करण नहीं होगा। –

1

आप इस तरह के पदानुक्रम में असाइन नहीं कर सकते हैं - बी और सी ए के विभिन्न उप-वर्ग हैं। आप बी को बी या सी को सी में असाइन कर सकते हैं लेकिन सी से बी या इसके विपरीत नहीं।

आप शायद बी 12 और सी में operator= को कार्यान्वित करना चाहते हैं, हालांकि यह कोशिश करने से पहले A::operator= पर असाइनमेंट के एक भाग को प्रतिनिधि करते हैं। अन्यथा उन वर्गों के बी-और सी-विशिष्ट भाग असाइनमेंट में खो जाएंगे।

+0

वास्तव में कोड विफल होने का कारण नहीं है। – Omnifarious

1

आम तौर पर, ऑपरेटर = बी में के रूप में

B& operator=(B const &); 

के बाद से बी 'सी' के एक स्पष्ट और सुलभ आधार नहीं है परिभाषित किया गया है, बी सी से रूपांतरण संकलक द्वारा अनुमति नहीं है।

तुम सच में एक 'सी' करना चाहते हैं 'बी' आप एक कक्षा में कॉपी-असाइनमेंट ऑपरेटर घोषित नहीं करते, 'बी' के रूप में

B& operator=(C const &); 
1

के संस्करण का उपयोग करके (शायद नहीं एक फिक्स & शायद नाम छुपा के आसपास काम कर सकते हैं नहीं है कि आप क्या करना चाहिए) परन्तु ... वहाँ एक तरह से आप अगर तुम सच में करना चाहिए मुद्दा मजबूर कर सकते हैं:

(A&)(*(&b)) = (A&)(*(&c)) 
संबंधित मुद्दे