2010-10-19 18 views
108

मैं एक हैडर फ़ाइल में निम्न कोड में आए:एक निजी शुद्ध वर्चुअल फ़ंक्शन का बिंदु क्या है?

class Engine 
{ 
public: 
    void SetState(int var, bool val); 
    { SetStateBool(int var, bool val); } 

    void SetState(int var, int val); 
    { SetStateInt(int var, int val); } 
private: 
    virtual void SetStateBool(int var, bool val) = 0;  
    virtual void SetStateInt(int var, int val) = 0;  
}; 

मेरे लिए, यह संकेत मिलता है कि या तो Engine वर्ग या एक वर्ग से व्युत्पन्न, उन शुद्ध आभासी कार्यों के लिए कार्यान्वयन प्रदान करने के लिए है। लेकिन मुझे नहीं लगता था कि व्युत्पन्न वर्गों को उन निजी कार्यों तक पहुंच मिल सकती है ताकि उन्हें पुन: कार्यान्वित किया जा सके - तो उन्हें आभासी क्यों बनाएं?

उत्तर

170

विषय में सवाल एक बहुत आम भ्रम का सुझाव देता है। भ्रम काफी आम है, C++ FAQ लंबे समय तक निजी वर्चुअल का उपयोग करने के खिलाफ वकालत की, क्योंकि भ्रम एक बुरी चीज लग रहा था।

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

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

"सार्वजनिक अतिभारित गैर Virtuals कॉल संरक्षित गैर अतिभारित Virtuals" (आश्चर्य, आश्चर्य!)

यह properly manage the hiding rule करने में मदद करता है। आप इसके बारे में here के बारे में अधिक पढ़ सकते हैं, लेकिन मैं इसे जल्द ही समझाने की कोशिश करूंगा।

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

class Engine 
{ 
public: 
    virtual void SetState(int var, bool val) {/*some implementation*/} 
    virtual void SetState(int var, int val) {/*some implementation*/} 
}; 

अब मान लेते हैं तो आप एक व्युत्पन्न वर्ग का निर्माण करना चाहते हैं और आप केवल विधि, कि तर्क के रूप में दो ints लेता है के लिए एक नया कार्यान्वयन प्रदान करने की आवश्यकता हैं।

class MyTurbochargedV8 : public Engine 
{ 
public: 
    // To prevent SetState(int var, bool val) from the base class, 
    // from being hidden by the new implementation of the other overload (below), 
    // you have to put using declaration in the derived class 
    using Engine::SetState; 

    void SetState(int var, int val) {/*new implementation*/} 
}; 

आप व्युत्पन्न वर्ग में घोषणा का उपयोग कर डाल करने के लिए (या दूसरा अधिभार को फिर से परिभाषित करने के लिए) भूल गए हैं, आप नीचे दिए गए परिदृश्य में मुसीबत में पड़ सकता है।

MyTurbochargedV8* myV8 = new MyTurbochargedV8(); 
myV8->SetState(5, true); 

आप Engine सदस्यों के छिपने की, बयान को रोकने नहीं दिए थे:

myV8->SetState(5, true); 

व्युत्पन्न वर्ग से void SetState(int var, int val) कहेंगे, int को true परिवर्तित।

इंटरफ़ेस आभासी नहीं है और आभासी कार्यान्वयन गैर सरकारी है, अपने उदाहरण के रूप में, व्युत्पन्न वर्ग के लेखक एक कम समस्या के बारे में सोचना है और बस

class MyTurbochargedV8 : public Engine 
{ 
private: 
    void SetStateInt(int var, int val) {/*new implementation*/} 
}; 
+6

+1 "पब्लिक ओवरलोडेड गैर-वर्चुअल कॉल संरक्षित गैर-ओवरलोडेड वर्चुअल" xD – GabLeRoux

+0

वर्चुअल फ़ंक्शन को निजी क्यों होना चाहिए? क्या यह सार्वजनिक हो सकता है? – Rich

+0

मुझे आश्चर्य है कि क्या उसके "वर्चुअल" लेख में हर्ब सटर द्वारा दिए गए दिशानिर्देश आज भी हैं? – nurabha

14

ठीक है, एक के लिए, यह एक व्युत्पन्न वर्ग को एक फ़ंक्शन को लागू करने की अनुमति देगा जो मूल वर्ग (शुद्ध वर्चुअल फ़ंक्शन घोषणा वाला) कॉल कर सकता है।

+3

लिख सकते हैं की तरह अगर ऐसा ** केवल ** बेस क्लास कॉल कर सकते हैं! –

3

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

एक संक्षिप्त विवरण DevX.com पाया जा सकता है।


संपादित एक निजी आभासी विधि प्रभावी ढंग से Template Method Pattern में प्रयोग किया जाता है। व्युत्पन्न कक्षाएं निजी वर्चुअल विधि को ओवरराइड कर सकती हैं लेकिन व्युत्पन्न कक्षाएं इसे बेस क्लास निजी वर्चुअल विधि (आपके उदाहरण में, SetStateBool और SetStateInt) पर कॉल नहीं कर सकती हैं। केवल बेस क्लास प्रभावी रूप से अपनी निजी आभासी विधि () को कॉल कर सकती है केवल तभी प्राप्त किए गए वर्गों को वर्चुअल फ़ंक्शन के मूल कार्यान्वयन का आह्वान करने की आवश्यकता होती है, वर्चुअल फ़ंक्शन को संरक्षित करें)।

Virtuality के बारे में एक दिलचस्प लेख मिल सकता है।

+0

@ गेंटलेमैन ... कॉलम डी बेनेट की टिप्पणी पर हम नीचे स्क्रॉल करें। ऐसा लगता है कि "एक निजी वर्चुअल फ़ंक्शन व्युत्पन्न कक्षाओं द्वारा ओवरराइड किया जा सकता है, लेकिन केवल बेस क्लास के भीतर से ही बुलाया जा सकता है।" @ माइकल गोल्डशेटिन भी ऐसा सोचता है। – BeeBand

+0

मुझे लगता है कि आप इस सिद्धांत को भूल गए हैं कि एक निजी आधारित वर्ग अपनी व्युत्पन्न कक्षा द्वारा नहीं देखा जा सकता है। वह ओओपी नियम है और यह ओओपी की सभी भाषाओं पर लागू होता है। व्युत्पन्न वर्ग के लिए अपनी बेस क्लास निजी वर्चुअल विधि को लागू करने के लिए, इसे बेस क्लास का 'मित्र' होना चाहिए। क्यूटी ने एक ही दृष्टिकोण लिया है जब उन्होंने अपना एक्सएमएल डॉम दस्तावेज़ मॉडल लागू किया था। –

+0

@ गेंटलमैन: नहीं, मैं नहीं भूल गया। मैंने अपनी टिप्पणी में एक टाइपो बनाया। "बेस क्लास विधियों तक पहुंच" के बजाय मुझे लिखा होगा कि "बेस क्लास विधियों को ओवरराइड कर सकते हैं"। व्युत्पन्न वर्ग निश्चित रूप से निजी वर्चुअल बेस क्लास विधि को ओवरराइड कर सकता है भले ही वह उस बेस क्लास विधि तक नहीं पहुंच सके। आपके द्वारा इंगित DevX.com आलेख गलत था (सार्वजनिक विरासत)। मेरे उत्तर में कोड आज़माएं। निजी वर्चुअल बेस क्लास विधि के बावजूद, व्युत्पन्न वर्ग इसे ओवरराइड करने में सक्षम है। आइए इसे आमंत्रित करने की क्षमता के साथ एक निजी वर्चुअल बेस क्लास विधि को ओवरराइड करने की क्षमता को भ्रमित न करें। – Void

36

निजी शुद्ध आभासी समारोह (ठीक है, यह बिल्कुल हमेशा शुद्ध आभासी नहीं है, लेकिन अभी भी आभासी वहाँ) गैर आभासी इंटरफेस मुहावरा का आधार है। बेशक, यह अन्य चीजों के लिए भी प्रयोग किया जाता है, लेकिन मुझे यह सबसे उपयोगी लगता है (: दो शब्दों में: एक सार्वजनिक समारोह में, आप शुरुआत में कुछ सामान्य चीजें (जैसे लॉगिंग, आंकड़े इत्यादि) डाल सकते हैं और समारोह के अंत में और फिर, "बीच में" इस निजी आभासी समारोह कॉल करने के लिए, कि विशिष्ट व्युत्पन्न वर्ग के लिए अलग होगा की तरह कुछ:।

class Base 
{ 
    // .. 
public: 
    void f(); 
private: 
    virtual void DerivedClassSpecific() = 0; 
    // .. 
}; 
void Base::f() 
{ 
    //.. Do some common stuff 
    DerivedClassSpecific(); 
    //.. Some other common stuff 
} 
// .. 

class Derived: public Base 
{ 
    // .. 
private: 
    virtual void DerivedClassSpecific(); 
    //.. 
}; 
void Derived::DerivedClassSpecific() 
{ 
    // .. 
} 

शुद्ध आभासी - बस व्युत्पन्न बाध्य कक्षाएं इसे लागू करने के

संपादित:। Wikipedia::NVI-idiom

: इस बारे में अधिक
2

संपादित करें: ओवरराइड करने और पहुंचने/उपयोग करने की क्षमता के बारे में स्पष्ट विवरण।

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

अपने कोड में लोगों की तरह एक आधार वर्ग में
#include <iostream> 

class Engine 
{ 
public: 
    void SetState(int var, bool val) 
    { 
    SetStateBool(var, val); 
    } 

    void SetState(int var, int val) 
    { 
    SetStateInt(var, val); 
    } 

private: 

    virtual void SetStateBool(int var, bool val) = 0; 
    virtual void SetStateInt(int var, int val) = 0; 

}; 

class DerivedEngine : public Engine 
{ 
private: 
    virtual void SetStateBool(int var, bool val) 
    { 
    std::cout << "DerivedEngine::SetStateBool() called" << std::endl; 
    } 

    virtual void SetStateInt(int var, int val) 
    { 
    std::cout << "DerivedEngine::SetStateInt() called" << std::endl; 
    } 
}; 


int main() 
{ 
    DerivedEngine e; 
    Engine * be = &e; 

    be->SetState(4, true); 
    be->SetState(2, 1000); 
} 

Privatevirtual तरीकों आम तौर पर Template Method design pattern लागू करने के लिए उपयोग किया जाता है। वह डिज़ाइन पैटर्न बेस क्लास में कोड को बदले बिना बेस क्लास में एल्गोरिदम के व्यवहार को बदलने की अनुमति देता है। उपरोक्त कोड जहां बेस क्लास पॉइंटर के माध्यम से बेस क्लास विधियों को बुलाया जाता है, वह टेम्पलेट विधि पैटर्न का एक साधारण उदाहरण है।

+0

मैं देखता हूं, लेकिन अगर व्युत्पन्न कक्षाओं में किसी तरह का उपयोग होता है, तो उन्हें निजी बनाने में परेशानी क्यों होती है? – BeeBand

+0

@BeeBand: उपयोगकर्ता को सार्वजनिक व्युत्पन्न क्लास वर्चुअल विधि ओवरराइड तक पहुंच होगी, लेकिन बेस क्लास तक पहुंच नहीं होगी। इस मामले में व्युत्पन्न वर्ग लेखक वर्चुअल विधि को निजी रूप से ओवरराइड रख सकता है। वास्तव में मैं उस पर जोर देने के लिए उपरोक्त नमूना कोड में परिवर्तन कर दूंगा। किसी भी तरह से, वे हमेशा निजी आधार वर्ग वर्चुअल विधियों को सार्वजनिक रूप से प्राप्त कर सकते हैं और ओवरराइड कर सकते हैं, लेकिन वे अभी भी केवल अपने व्युत्पन्न वर्ग वर्चुअल विधियों तक पहुंच सकते हैं। ध्यान दें कि मैं एक भेदभाव between ओवरराइड और पहुंच/आमंत्रण बना रहा हूँ। – Void

+0

दिलचस्प। डाउनवॉट्स क्यों? यदि आप डाउनवोट करने जा रहे हैं, तो कृपया एक टिप्पणी में क्यों समझाएं ताकि पोस्टर और अन्य आपकी राय से लाभ उठा सकें। – Void

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