2010-08-19 10 views
8

वास्तव में एक छोटा सा परेशानता है क्योंकि मैं 'उपयोग' कीवर्ड का उपयोग करने के बजाय व्युत्पन्न फ़ंक्शन को लपेटकर समस्या के आसपास काम कर सकता हूं लेकिन निम्न कार्य क्यों नहीं करता है (संकलक मुझे बताता है कि 'get_elem' अभी भी 'बार' वर्ग में शुद्ध आभासी है)।एक अलग विरासत विधि 'उपयोग' द्वारा 'शुद्ध' वर्चुअल फ़ंक्शंस ओवरराइटिंग

class Elem {}; 

class DerivedElem : public Elem {}; 

class Foo { 

    public: 
    virtual Elem& get_elem() = 0; 

}; 

class Goo { 

    protected: 
    DerivedElem elem; 

    public: 
    DerivedElem& get_elem() { return elem; } 

}; 


class Bar : public Foo, public Goo { 

    public: 
    using Goo::get_elem; 

}; 

int main(void) { 

    Bar bar; 

} 

चीयर्स,

टॉम

+4

साफ, छोटा, आत्मनिर्भर उदाहरण। मैं चाहता हूं कि जिनके पास कोड कोड था, उन्होंने जो किया वह सब किया। – Omnifarious

+0

क्या कॉवरेन्ट रिटर्न प्रकारों के लिए गुओ को फू से नहीं लिया जाना चाहिए? – Chubsdad

+0

हाय दोस्तों, त्वरित और सहायक प्रतिक्रियाओं के लिए धन्यवाद। पॉलिमॉर्फिक असाइनमेंट स्टीव के बारे में अच्छी युक्ति। यदि कोई सोच रहा था कि मैं इस तरह की संरचना क्यों देख रहा हूं, तो मेरे वास्तविक कोड 'गू' में एक टेम्पलेट क्लास है जिसे बाद के टेम्पलेट वर्गों द्वारा विरासत में मिला है और 'फू' श्रेणी वर्गों तक पहुंचने के लिए 'इंटरफ़ेस' है। – Tom

उत्तर

2

अपने उदाहरण में, फू और गू अलग वर्ग हैं। बार में, गुओ से get_elem विधि Foo में एक के साथ समान नहीं है, भले ही उनके हस्ताक्षर मैच हों।

using Goo::get_elem होने के बाद, आप बस गोइल में get_elem() को अयोग्य कॉल को हल करने के लिए संकलक को बताते हैं।

5

यदि गो एक इंटरफ़ेस फू को किसी विशेष तरीके से कार्यान्वित करने के लिए डिज़ाइन किया गया "मिश्रण" है (अन्य कार्यान्वयन के साथ अन्य मिश्रण भी हो सकते हैं), तो गो फू से प्राप्त कर सकता है (बार ऐसा करने के बजाय)।

यदि गो इंटरफ़ेस फू को कार्यान्वित करने के लिए डिज़ाइन नहीं किया गया है, तो यह बार का इलाज करने के लिए एक भयानक गलती होगी, हालांकि यह शुद्ध वर्चुअल फ़ंक्शन लागू किया गया था, जब यह वास्तव में एक ही हस्ताक्षर का कार्य होता है । यदि आप सी ++ में अंतर्निहित इंटरफेस और "बतख" टाइपिंग चाहते हैं तो आप इसे कर सकते हैं, लेकिन आपको इसे टेम्पलेट्स के साथ करना होगा। सही या गलत तरीके से, शुद्ध वर्चुअल फ़ंक्शंस स्पष्ट रूप से घोषित इंटरफ़ेस के लिए हैं, और Goo के get_elem फ़ंक्शन को स्पष्ट रूप से Foo::get_elem लागू करने के लिए घोषित नहीं किया गया है। तो यह नहीं करता है।

मुझे लगता है कि यह समझ में नहीं आता है कि क्यों भाषा में using Goo::get_elem for Foo;, या बार में कुछ ऐसी घोषणा को परिभाषित नहीं किया जा सकता है, ताकि कॉल को लपेटने के लिए बहुत सारे बॉयलरप्लेट को शामिल किया जा सके।

आप शायद Goo वास्तव में फू के बारे में जानने के बिना, कुछ हद तक इस का समर्थन करने के लिए अनुमति देने के खाके के साथ कुछ कर सकते हैं:

template <typename T> 
class Goo : public T { 

    protected: 
    DerivedElem elem; 

    public: 
    DerivedElem& get_elem() { return elem; } 
}; 

class Bar : public Goo<Foo> {}; 

class Baz : public Goo<Fuu> {}; 

कहाँ Fuu एक get_elem समारोह है कि कुछ अन्य इंटरफेस है। जाहिर है कि यह Bar के लेखक की ज़िम्मेदारी है कि Goo वास्तव में Foo के अनुबंध को लागू करता है, और Baz के लिए Fuu के अनुबंध की जांच करता है।

वैसे, इस प्रकार का कॉन्वर्सिस थोड़ा सा डोडी है। फू को देखकर, कोई व्यक्ति bar.get_elem() = Elem() अभिव्यक्ति की अपेक्षा कर सकता है, और ऐसा नहीं है, इसलिए एलएसपी का उल्लंघन किया जाता है। संदर्भ इस तरह मजाकिया हैं। ((Foo &)bar).get_elem() = Elem() मान्य है लेकिन सामान्य रूप से काम नहीं करता है! यह केवल Elem उप-ऑब्जेक्ट को असाइन करता है, और उस मामले के लिए ((Foo &)bar).get_elem() = DerivedElem() करता है। पॉलिमॉर्फिक असाइनमेंट मूल रूप से एक उपद्रव है।

1

आपको सी ++ के कई अजीब कोनों में से एक का सामना करना पड़ा है। इस मामले में सी ++ अलग-अलग वर्गों से विरासत में दो वर्चुअल फ़ंक्शंस को समान कार्य करने के लिए नहीं मानता है, भले ही उनके पास समान नाम और प्रकार हस्ताक्षर हो।

इस तरह से कार्य करने के लिए सी ++ के कुछ अच्छे कारण हैं। उदाहरण के लिए, अक्सर यह मामला होता है कि वास्तव में वही नाम और प्रकार हस्ताक्षर के बावजूद वे दो कार्य वास्तव में समान नहीं हैं। दो कार्यों का अर्थपूर्ण अर्थ अलग है।

यहाँ एक उदाहरण है:

namespace vendor1 { 

class Circle { 
public: 
    virtual ::std::size_t size() const { return sizeof(*this); } 
}; 

} // namespace vendor1 


namespace vendor2 { 

class Shape { 
public: 
    virtual double size() const = 0; 
}; 

class Circle : public Shape { 
public: 
    virtual double size() const { return radius_ * radius_ * M_PI; } 
}; 

} // namespace vendor2 

और फिर आप इस प्रयास करें:

namespace my_namespace { 

class Circle : public ::vendor1::Circle, public ::vendor2::Circle { 
// Oops, there is no good definition for size 
}; 

तो आप इस का सहारा लेना है:

namespace my_namespace { 

class Vendor1Circle : public ::vendor1::Circle { 
public: 
    virtual ::std::size_t data_structure_size() const { return size(); } 
}; 

class Vendor2Circle : public ::vendor2::Circle { 
public: 
    virtual double area() const { return size(); } 
}; 

class Circle : public Vendor1Circle, public Vendor2Circle { 
// Now size is still ambiguous and should stay that way 
// And in my opinion the compiler should issue a warning if you try 
// to redefine it 
}; 

तो, सी ++ अच्छा कारण है एक ही प्रकार के हस्ताक्षर के साथ आभासी कार्यों का इलाज करने के लिए (वापसी प्रकार प्रकार हस्ताक्षर का हिस्सा नहीं है) और दो differe से नाम विभिन्न कार्यों के रूप में एनटी आधार।

जहां तक ​​using चला जाता है ... सभी using निर्देश कहते हैं, "इस नामस्थान से इस नामस्थान में नाम जोड़ें जैसे कि यहां घोषित किया गया था।"। जहां तक ​​आभासी कार्यों का संबंध है, यह एक शून्य अवधारणा है। यह केवल सुझाव देता है कि नाम का उपयोग करते समय किसी भी अस्पष्टता को एक अलग तरीके से हल किया जाना चाहिए। यह केवल एक नाम घोषित करता है, यह नाम परिभाषित नहीं करता है। वर्चुअल फ़ंक्शन को ओवरराइड करने के लिए एक नई परिभाषा की आवश्यकता होती है।

OTOH, अगर आप इस तरह इनलाइन एक सरल thunk परिभाषा में डाल अगर:

class Bar : public Foo, public Goo { 

    public: 
    virtual DerivedElem& get_elem() { return Goo::get_elem(); } 
}; 

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

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