2012-07-24 17 views
33

क्यों इस संकलन करता है:एक और उपवर्ग में एक आधार वर्ग के एक संरक्षित सदस्य तक पहुँचने

class FooBase 
{ 
protected: 
    void fooBase(void); 
}; 

class Foo : public FooBase 
{ 
public: 
    void foo(Foo& fooBar) 
    { 
     fooBar.fooBase(); 
    } 
}; 

लेकिन यह नहीं करता है?

class FooBase 
{ 
protected: 
    void fooBase(void); 
}; 

class Foo : public FooBase 
{ 
public: 
    void foo(FooBase& fooBar) 
    { 
     fooBar.fooBase(); 
    } 
}; 

उस वर्ग के सभी मामलों के लिए निजी/संरक्षित सदस्यों के लिए एक हाथ सी ++ की एक्सेस प्रदान पर, लेकिन दूसरी ओर यह एक उपवर्ग के सभी उदाहरणों के लिए एक आधार वर्ग के संरक्षित सदस्यों को एक्सेस नहीं मिलता। यह मेरे लिए असंगत दिखता है।

मैंने वीसी ++ और ideone.com के साथ संकलन का परीक्षण किया है और दोनों पहले संकलित करते हैं लेकिन दूसरा कोड स्निपेट नहीं।

+0

@iammilind क्या आप वाकई सही प्रश्न बंद कर रहे हैं? या यह चारों ओर एक और तरीका होना चाहिए? – Eiko

+0

@Eiko मूल रूप से मैंने इस के साथ दूसरे को बंद कर दिया। तब मैंने पाया कि वर्तमान में उत्तर अधिक विस्तृत और मानक के साथ उद्धृत किया गया था। इसलिए इसे फिर से खोल दिया और इसे बंद कर दिया। – iammilind

उत्तर

28

जब fooFooBase संदर्भ प्राप्त करता है, तो संकलक यह नहीं जानता कि तर्क Foo का वंशज है, इसलिए इसे मानना ​​है कि यह नहीं है। Foo के पास अन्य Foo ऑब्जेक्ट्स, अन्य सभी भाई कक्षाओं के विरासत में सुरक्षित सदस्यों तक पहुंच है। मनमाने ढंग से FooBase वंश के संरक्षित सदस्यों कॉल कर सकते हैं

class FooSibling: public FooBase { }; 

FooSibling sib; 
Foo f; 
f.foo(sib); // calls sib.fooBase()!? 

तो Foo::foo, तो यह FooSibling की रक्षा की विधि है, जो Foo करने के लिए कोई सीधा संबंध नहीं है कॉल कर सकते हैं:

इस कोड पर विचार करें। ऐसा नहीं है कि संरक्षित पहुंच कैसे काम की जानी चाहिए।

Foo जरूरतों सभी FooBase वस्तुओं के संरक्षित सदस्यों, के लिए पहुंचते हैं तो न सिर्फ उन है कि यह भी Foo सन्तान, तो Foo जरूरतों के लिए जाना जाता FooBase के एक दोस्त होने के लिए:

class FooBase 
{ 
protected: 
    void fooBase(void); 
    friend class Foo; 
}; 
+0

आह, आपके उदाहरण कोड के कारण, इसकी अनुमति क्यों नहीं है, यह स्पष्ट है। धन्यवाद। – Kaiserludi

+0

मुझे आपका उदाहरण नहीं मिला है। यदि fooBase वर्चुअल नहीं है, तो आपको जो मिलता है वह है FooBase :: fooBase, जो आप वैसे भी इंगित करते हैं। यदि fooBase वर्चुअल है, तो आप वास्तव में FooSibling :: fooBase को कॉल कर रहे हैं, लेकिन यह भी कारण है कि आप वर्चुअल फ़ंक्शंस का उपयोग क्यों कर रहे हैं: वास्तविक ऑब्जेक्ट को फ़ंक्शंस को अनुकूलित करने में सक्षम होने के लिए? मैं नहीं देखता कि यह व्यवहार एक समस्या है। –

+0

आभासी यहां अप्रासंगिक है, @ विन्सेंट। एक सदस्य की आभासी स्थिति इस बात को प्रभावित नहीं करती है कि किसके नाम का उपयोग करने की अनुमति है। सदस्य की * दृश्यता * यह निर्धारित करती है कि उसका नाम किसके उपयोग कर सकता है। 'फू' अन्य ज्ञात 'फू' ऑब्जेक्ट्स के संरक्षित सदस्यों को देख सकता है। यह किसी भी अन्य वस्तु के संरक्षित सदस्यों को नहीं देख सकता है, यहां तक ​​कि उनके पूर्वजों के वर्ग से संबंधित नहीं, क्योंकि उन पूर्वजों को आवश्यक नहीं है * ज्ञात * 'फू' होना चाहिए। सत्यता और दृश्यता सी ++ में ऑर्थोगोनल अवधारणाएं हैं (लेकिन अन्य भाषाओं में जरूरी नहीं)। –

3

दोनों उदाहरणों में Foo एक संरक्षित विधि fooBase विरासत में मिलता है। हालांकि, आपके पहले उदाहरण में आप एक ही कक्षा (Foo::foo कॉल Foo::fooBase पर कॉल) से दी गई संरक्षित विधि तक पहुंचने का प्रयास करते हैं, जबकि दूसरे उदाहरण में आप किसी अन्य वर्ग से संरक्षित विधि तक पहुंचने का प्रयास करते हैं जिसे मित्र वर्ग के रूप में घोषित नहीं किया जाता है (Foo::foo प्रयास करता है FooBase::fooBase पर कॉल करने के लिए, जो विफल रहता है, बाद में संरक्षित है)।

1

पहले उदाहरण में आप प्रकार फू का ऑब्जेक्ट पास करें, जो स्पष्ट रूप से विधि fooBase() को विरासत में लेता है और इसलिए इसे कॉल करने में सक्षम है। दूसरे उदाहरण में आप संरक्षित फ़ंक्शन को कॉल करने का प्रयास कर रहे हैं, बस इस पर ध्यान दिए बिना कि आप किस संदर्भ में एक क्लास इंस्टेंस से संरक्षित फ़ंक्शन को कॉल नहीं कर सकते हैं, जहां इसकी घोषणा की गई है। पहले उदाहरण में आप संरक्षित विधि fooBase का उत्तराधिकारी हैं, और इसलिए आपको इसे FIN संदर्भ

1

पर कॉल करने का अधिकार है, मुझे अवधारणाओं और संदेशों के संदर्भ में चीजें देखने लगती हैं। यदि आपकी FooBase विधि को वास्तव में "SendMessage" कहा जाता था और Foo "EnglishSpeakingPerson" था और FooBase SpeakingPerson था, तो आपके संरक्षित घोषणा का उद्देश्य SendMessage को अंग्रेजी स्पीकिंगपर्सन (और उपclasses: AmericanEnglishSpeakingPerson, AustralianEnglishSpeakingPerson) के बीच प्रतिबंधित करना है। स्पीकिंगपर्सन से व्युत्पन्न एक अन्य प्रकार का फ्रेंचस्पेकिंगसन एक प्रेषण प्राप्त नहीं कर पाएगा, जब तक कि आपने फ्रांसीसी स्पीकिंगपर्सन को एक दोस्त के रूप में घोषित नहीं किया, जहां 'मित्र' का अर्थ था कि फ्रांसीसी स्पीकिंगरसन के पास अंग्रेजी स्पीकिंगपर्सन (यानी अंग्रेजी को समझ सकते हैं) से SendMessage प्राप्त करने की विशेष क्षमता है।

9

मुख्य बिंदु यह है कि protected आपको में किसी भी अन्य सदस्यों में सदस्यों की अपनी प्रतिलिपि तक पहुंच प्रदान करता है। यह एक आम गलत धारणा है, जितनी बार हम सामान्यीकृत नहीं करते हैं और protected सदस्य को व्युत्पन्न प्रकार तक पहुंच प्रदान करते हैं (स्पष्ट रूप से केवल अपने स्वयं के आधार पर बताते हुए ...)

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

class base { 
protected: 
    LargeData data; 
// ... 
public: 
    virtual int result() const;  // expensive calculation 
    virtual void modify();   // modifies data 
}; 
class cache_on_read : base { 
private: 
    mutable bool cached; 
    mutable int cache_value; 
// ... 
    virtual int result() const { 
     if (cached) return cache_value; 
     cache_value = base::result(); 
     cached = true; 
    } 
    virtual void modify() { 
     cached = false; 
     base::modify(); 
    } 
}; 
class cache_on_write : base { 
    int result_value; 
    virtual int result() const { 
     return result_value; 
    } 
    virtual void modify() { 
     base::modify(); 
     result_value = base::result(); 
    } 
}; 

cache_on_read प्रकार के डेटा में किए गए संशोधन कब्जा और अमान्य के रूप में परिणाम के निशान, ताकि अगले मूल्य पुनर्मूल्यांकन के पढ़े। यह एक अच्छा तरीका है यदि लेखन की संख्या अपेक्षाकृत अधिक है, क्योंकि हम केवल मांग पर गणना करते हैं (यानी कई संशोधित पुनर्मूल्यांकन को ट्रिगर नहीं करेंगे)। cache_on_write परिणाम को पहले से सटीक करता है, जो कि एक अच्छी रणनीति हो सकती है यदि लेखन की संख्या छोटी है, और आप पढ़ने के लिए निर्धारिती लागत चाहते हैं (पढ़ने पर कम विलंबता सोचें)।

अब, मूल समस्या पर वापस जाएं। दोनों कैश रणनीतियों आधार की तुलना में इनवेरिएंट का एक कठोर सेट बनाए रखते हैं। पहले मामले में, अतिरिक्त आविष्कार यह है कि cachedtrue केवल data अंतिम पढ़ने के बाद संशोधित नहीं किया गया है। दूसरे मामले में, अतिरिक्त आविष्कार यह है कि result_value हर समय ऑपरेशन का मूल्य है।

तो एक तिहाई व्युत्पन्न प्रकार एक base के लिए एक संदर्भ लिया और data पहुँचा लिखने के लिए (यदि protected यह करने की अनुमति दी), तो यह व्युत्पन्न प्रकार के अपरिवर्तनशीलताओं साथ टूट जाएगा।

कहा जा रहा है कि, भाषा का विनिर्देश टूटा (व्यक्तिगत राय) है क्योंकि यह उस विशेष परिणाम को प्राप्त करने के लिए एक पिछवाड़े छोड़ देता है। विशेष रूप से, यदि आप व्युत्पन्न प्रकार में किसी आधार से किसी सदस्य के सदस्य को पॉइंटर बनाते हैं, तो derived में एक्सेस की जांच की जाती है, लेकिन लौटा हुआ पॉइंटर base के सदस्य के लिए एक सूचक है, जिसे परbase ऑब्जेक्ट पर लागू किया जा सकता है:

class base { 
protected: 
    int x; 
}; 
struct derived : base { 
    static void modify(base& b) { 
     // b.x = 5;      // error! 
     b.*(&derived::x) = 5;    // allowed ?!?!?! 
    } 
} 
19

C++ FAQ अच्छी तरह से इस मुद्दे सार रखते हैं:

[आप] अपनी स्वयं की जेब लेने के लिए अनुमति दी जाती है, लेकिन आप अपने पिता की जेब और न ही अपने भाई की जेब लेने के लिए अनुमति नहीं है।

+8

आपको अपने बेटे के जेब चुनने की अनुमति है –

0

hobo's answer के अतिरिक्त आप एक कामकाज की तलाश कर सकते हैं।

यदि आप चाहते हैं कि सबक्लास fooBase विधि को कॉल करना चाहते हैं तो आप इसे static बना सकते हैं। स्थिर तर्क विधियों को सभी तर्कों के साथ उप-वर्गों द्वारा पहुंचा जा सकता है।

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