एक निजी शुद्ध आभासी फ़ंक्शन का क्या मतलब है?


139

मैं हेडर फ़ाइल में निम्नलिखित कोड भर आया:

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वर्ग या उससे प्राप्त वर्ग, उन शुद्ध आभासी कार्यों के लिए कार्यान्वयन प्रदान करना है। लेकिन मुझे नहीं लगा कि व्युत्पन्न वर्ग उन निजी कार्यों तक पहुंच बना सकता है ताकि उन्हें फिर से लागू किया जा सके - इसलिए उन्हें आभासी क्यों बनाया जाए?

जवाबों:


209

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

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

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

"सार्वजनिक अतिभारित गैर-आभासी कॉल संरक्षित गैर-अतिभारित आभासी"

यह छिपने के नियम को ठीक से प्रबंधित करने में मदद करता है । आप इसके बारे में यहाँ और अधिक पढ़ सकते हैं , लेकिन मैं इसे शीघ्र ही समझाने की कोशिश करूँगा।

कल्पना कीजिए, 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 )व्युत्पन्न वर्ग से कॉल करने के trueलिए परिवर्तित होगा int

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

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

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

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

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

43

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

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()
{
    // ..
}

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

EDIT : इसके बारे में और अधिक: विकिपीडिया :: एनवीआई-मुहावरा


17

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


5
कि केवल आधार वर्ग कॉल कर सकते हैं!
अंडरस्कोर_ड

4

EDIT: ओवरराइड करने की क्षमता और एक्सेस / इनवोक करने की क्षमता के बारे में स्पष्ट कथन।

यह उन निजी कार्यों को ओवरराइड करने में सक्षम होगा। उदाहरण के लिए, निम्नलिखित उल्लिखित उदाहरण काम करता है ( EDIT: व्युत्पन्न वर्ग विधि निजी, और 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);
}

Private virtualबेस कोड की विधियाँ जैसे आपके कोड में आमतौर पर टेम्प्लेट विधि डिजाइन पैटर्न को लागू करने के लिए उपयोग की जाती हैं । वह डिजाइन पैटर्न बेस क्लास में कोड को बदले बिना बेस क्लास में एक एल्गोरिथ्म के व्यवहार को बदलने की अनुमति देता है। उपरोक्त कोड जहां बेस क्लास पॉइंटर को बेस क्लास पॉइंटर के माध्यम से लागू किया जाता है, खाका विधि पैटर्न का एक सरल उदाहरण है।


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

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

क्योंकि तुम गलत हो। कक्षाओं के बीच विरासत की दृश्यता Engineऔर DerivedEngineउस चीज़ के लिए DerivedEngine(या एक्सेस) को ओवरराइड (या एक्सेस) नहीं कर सकता है, जिसके साथ कुछ नहीं करना है ।
विल्हेमटेल

@ विल्हेमटेल: आह आप निश्चित रूप से सही हैं। मैं उसी हिसाब से अपना जवाब अपडेट करूंगा।
शून्य

3

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

एक संक्षिप्त विवरण देवएक्स.कॉम से पाया जा सकता है ।


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

आभासीता के बारे में एक दिलचस्प लेख पाया जा सकता है ।


2
@ जेंटलमैन ... हम्म कोलिन डी बेनेट की टिप्पणी के लिए नीचे स्क्रॉल करें। उन्हें लगता है कि "एक निजी आभासी कार्य को व्युत्पन्न वर्गों द्वारा ओवरराइड किया जा सकता है, लेकिन केवल आधार वर्ग के भीतर से ही बुलाया जा सकता है।" @ मिचेल गोल्डस्मिन भी ऐसा ही सोचते हैं।
बीबैंड

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

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

@ जेंटलमैन: @wilhelmtell ने मेरे उत्तर / टिप्पणी में त्रुटि बताई। बेस क्लास पद्धति की व्युत्पन्न वर्ग पहुंच को प्रभावित करने वाली विरासत के बारे में मेरा दावा बंद था। मैंने आपके उत्तर के लिए आपत्तिजनक टिप्पणी को हटा दिया है।
शून्य

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

0

TL; DR उत्तर:

आप इसे दूसरे स्तर के एनकैप्सुलेशन की तरह मान सकते हैं - कहीं संरक्षित और निजी के बीच : आप इसे चाइल्ड क्लास से नहीं कह सकते, लेकिन आप इसे ओवरराइड कर सकते हैं।

टेम्प्लेट मेथड डिजाइन पैटर्न को लागू करते समय यह उपयोगी है । आप संरक्षित का उपयोग कर सकते हैं , लेकिन बेहतर एन्कैप्सुलेशन के कारण वर्चुअल के साथ-साथ निजी को बेहतर विकल्प माना जा सकता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.