2011-03-30 10 views
5

मेरे पास एकाधिक माता-पिता के साथ सी ++ कक्षा है; हर माता-पिता एक आम नाम है लेकिन एक अलग उद्देश्य के साथ एक समारोह को परिभाषित करता है:योग्य वर्चुअल तरीके ओवरराइडिंग

class BaseA 
{ 
    virtual void myFunc(); // does some task 
}; 
class BaseB 
{ 
    virtual void myFunc(); // does some other task 
}; 
class Derived : public BaseA, public BaseB; 

तो यह है कि यह किया गया था, मुझे कोई समस्या नहीं होता - मैं अस्पष्टता यह एक कथन का उपयोग के साथ हल कर सकता है, और मैं चुन सकते हैं जो बेस क्लास नाम और स्कोप रिज़ॉल्यूशन ऑपरेटर का उपयोग करके कॉल करने के लिए एक।

दुर्भाग्य से, व्युत्पन्न वर्ग उन्हें ओवरराइड करने की जरूरत है दोनों:

class Derived : public BaseA, public BaseB 
{ 
    virtual void BaseA::myFunc(); // Derived needs to change the way both tasks are done 
    virtual void BaseB::myFunc(); 
} 

यह काम नहीं करता है, इसलिए नहीं कि यह एक नया अस्पष्टता का परिचय (हालांकि यह हो सकता है), लेकिन क्योंकि

" त्रुटि C3240: 'myFunc': 'बेसए' 'का एक गैर-अधिभारित अमूर्त सदस्य फ़ंक्शन होना चाहिए "

" त्रुटि C2838: सदस्य घोषणा में अवैध योग्य नाम "

विभिन्न परिस्थितियों में मैं केवल विधियों का नाम बदल सकता हूं, या संकलक के सुझाव के रूप में उन्हें शुद्ध आभासी बना सकता हूं। हालांकि, वर्ग पदानुक्रम संरचना और कई बाहरी मुद्दों ने पहला विकल्प अत्यंत कठिन बना दिया है, और दूसरा असंभव है।

क्या किसी के पास कोई सुझाव है? क्वालीफायर केवल शुद्ध आभासी तरीकों के लिए क्यों अनुमति दी जाती है? वर्चुअल विधियों को एक साथ ओवरराइड करने और अस्पष्टताओं को हल करने का कोई तरीका है?

+0

आप अलग-अलग वर्गों में व्यवहार को ओवरराइड भी कर सकते हैं: 'DerivedA: Public BaseA',' DerivedB: Public BaseB', और फिर 'व्युत्पन्न: सार्वजनिक DerivedA, सार्वजनिक DerivedB'। यह अस्पष्टता समस्या को हल नहीं करता है, हालांकि – Cameron

उत्तर

3

Microsoft allows that syntax (यह विजुअल सी ++ 2005 में उपलब्ध है)। They also introduced a new, more powerful syntax for managed code only

कोई भी सी ++ 0x में शामिल नहीं किया गया था।

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2108.html


देखें मुझे लगता है कि यह एक समाधान नहीं है:

class BaseA 
{ 
protected: 
    virtual void myFunc(); // does some task 
}; 
class BaseB 
{ 
protected: 
    virtual void myFunc(); // does some other task 
}; 
class ShimA : virtual BaseA 
{ 
    virtual void myFunc() { myFuncA(); } 
protected: 
    virtual void myFuncA() { BaseA::myFunc(); } 
}; 
class ShimB : virtual BaseB 
{ 
    virtual void myFunc() { myFuncB(); } 
protected: 
    virtual void myFuncB() { BaseB::myFunc(); } 
}; 
class Derived : public virtual BaseA, public virtual BaseB, protected ShimA, protected ShimB 
{ 
    virtual void myFuncA() {} 
    virtual void myFuncB() {} 
}; 
+0

एमएस वाक्यविन्यास पर जानकारी के लिए धन्यवाद; दुर्भाग्य से यह केवल सार आधार वर्गों के साथ काम करता है (उनके उदाहरण में "__interface" कीवर्ड को नोट करें)। – John

+0

@ जॉन: प्रबंधित संस्करण आधार कक्षाओं से प्राप्त वर्चुअल विधियों के लिए काम करता है। वैसे भी, मेरे संपादन पर एक नज़र डालें, मुझे लगता है कि यह आपकी समस्या का समाधान करना चाहिए। –

+0

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

1

यह multiple inheritance के साथ बड़े मुद्दों में से एक है। हमेशा ऐसे मुद्दे होते हैं जब आपके पास विरासत में एक ही नाम के एकाधिक कार्य होते हैं, यह निर्धारित करने के लिए कि कौन से को ओवरराइड किया जाना चाहिए The Diamond Problem देखें। आप दोनों को ओवरराइड नहीं कर सकते क्योंकि फ़ंक्शन सिंटैक्स (फ़ंक्शन नाम और ऑपरेटर) अद्वितीय होना चाहिए।

1

आप एक रचना ऑब्जेक्ट का उपयोग कर सकते हैं।

class Derived : public BaseB {   
    struct temp : public BaseA { 
     virtual void myFunc() { 
      d->BaseAMyFunc(); 
     } 
     Derived* d; 
    }; 
    temp t; 
public: 
    Derived() { 
     t.d = this; 
    } 
    operator BaseA&() { return temp; } 
    operator const BaseA&() const { return temp; } 
    void myFunc(); // refers to BaseB::myFunc() 
    void BaseAMyFunc(); // called when BaseA::myFunc() is called. 
} 

यह विशेष रूप से साफ नहीं है और यह कुछ हद तक प्रतिबंधित है, लेकिन यह कुछ परिस्थितियों काम करता है।

0

मुझे लगता है कि इस सवाल का वर्ष है, लेकिन यह विचारों का एक बहुत था है और वहाँ हल करने के लिए एक साफ रास्ता है यह अगर आप इंटरफेस के लेखक हैं।

कई लोग मानते हैं कि वर्चुअल इंटरफेस में सार्वजनिक गैर वर्चुअल फ़ंक्शन होना चाहिए जो आंतरिक वर्चुअल फ़ंक्शंस को आंतरिक रूप से रोकता है (मैं उनके साथ सहमत हूं)।

struct BaseA 
{ 
    virtual ~BaseA() = default; 

    void func() 
    { 
    handle_a_func(); 
    } 

private: 
    virtual void handle_a_func() = 0; 
}; 

struct BaseB 
{ 
    virtual ~BaseB() = default; 

    int func() const // note the different signature and return type 
    { 
    handle_b_func(); 
    } 

private: 
    virtual int handle_b_func() const = 0; 
}; 

// now provide an implementation 

struct ABImpl : public BaseA, public BaseB 
{ 
    ABImpl() {} 

private: 
    void handle_a_func() override final 
    { 
    // alter some state 
    } 

    int handle_b_func() const override final 
    { 
    return _x; 
    } 

    int _x = 0; 
};   

// now use the class 
auto ab = make_shared<ABImpl>(); 

auto a = static_pointer_cast<BaseA>(ab); 
auto b = static_pointer_cast<const BaseB>(ab); 

a->func(); // uses A's mutable interface 
auto x = b->func(); // uses B's const interface 

इस दृष्टिकोण का एक और लाभ यह है कि आधार वर्ग कार्यान्वयन की तरह चीजों का प्रबंधन कर सकते हैं: यह कुछ लाभ, उन्हें जा रहा है गैर आभासी नाम अलग अर्थ हो सकते हैं कि जब से वे और अधिक दृढ़ता से इंटरफ़ेस करने के लिए बाध्य कर रहे हैं में से एक है mutexes और व्युत्पन्न कार्यों की ओर से प्रहरी, उदाहरण के लिए:

struct base 
{ 
    void write_to(std::ostream& os) const { 
    // lock a mutex here? 
    handle_write(os); 
    private: 
    virtual void handle_write(std::ostream& os) const { 
     os << "write base class info here\n"; 
    } 
}; 

inline std::ostream& operator<<(std::ostream& os, const base& b) { 
    b.write_to(os); 
    return os; 
} 

struct derived : base { 
private: 
    virtual void handle_write(std::ostream& os) const override { 
    base::handle_write(os); // handle base class implementation 
    std::cout << "write relevant data here. We could still be overridden safely\n"; 
    } 
}; 

// write any derived class like this: 
auto b = unique_ptr<base> { new derived() }; 
cout << "here is a kind-of b:\n" << *b << endl; 
:

struct base { 

    void do() { 
    std::unique_lock<std::mutex> l(_m); 
    handle_do(); 
    } 

private: 
    virtual void handle_do() = 0; 

    std::mutex _m; 
}; 

फिर भी एक और लाभ यह है कि कुछ उपयोगी मुक्त समारोह ऑपरेटरों केवल पूरे वर्ग पदानुक्रम के लिए एक बार में परिभाषित किया जा की जरूरत है

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