मुझे C ++ प्राइवेट इनहेरिटेंस का उपयोग कब करना चाहिए?


116

संरक्षित विरासत के विपरीत, C ++ निजी विरासत ने मुख्यधारा C ++ के विकास में अपना रास्ता खोज लिया। हालाँकि, मुझे अभी भी इसके लिए एक अच्छा उपयोग नहीं मिला है।

आप लोग इसका उपयोग कब करते हैं?

c++  oop 

जवाबों:


60

उत्तर स्वीकृति के बाद ध्यान दें: यह पूर्ण उत्तर नहीं है। यदि आप प्रश्न में रुचि रखते हैं तो अन्य उत्तर यहां (वैचारिक रूप से) और यहां (सिद्धांत और व्यावहारिक दोनों ) पढ़ें । यह सिर्फ एक फैंसी चाल है जिसे निजी विरासत के साथ हासिल किया जा सकता है। जबकि यह फैंसी है यह सवाल का जवाब नहीं है।

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

class ClassSealer {
private:
   friend class Sealed;
   ClassSealer() {}
};
class Sealed : private virtual ClassSealer
{ 
   // ...
};
class FailsToDerive : public Sealed
{
   // Cannot be instantiated
};

मुहरबंद को तत्काल किया जा सकता है। यह क्लाससेलर से प्राप्त होता है और निजी कंस्ट्रक्टर को सीधे कॉल कर सकता है क्योंकि यह एक दोस्त है।

FailsToDerive संकलन नहीं के रूप में यह कॉल करना होगा ClassSealer निर्माता सीधे (आभासी विरासत आवश्यकता), लेकिन यह उस में निजी है नहीं के रूप में कर सकते हैं सील वर्ग और इस मामले में FailsToDerive के एक दोस्त नहीं है ClassSealer


संपादित करें

टिप्पणियों में यह उल्लेख किया गया था कि सीआरटीपी का उपयोग करते समय इसे सामान्य नहीं बनाया जा सकता था। C ++ 11 मानक टेम्पलेट तर्कों से दोस्ती करने के लिए एक अलग सिंटैक्स प्रदान करके उस सीमा को हटा देता है:

template <typename T>
class Seal {
   friend T;          // not: friend class T!!!
   Seal() {}
};
class Sealed : private virtual Seal<Sealed> // ...

बेशक यह सब लूट है, क्योंकि C ++ 11 finalवास्तव में इस उद्देश्य के लिए एक प्रासंगिक कीवर्ड प्रदान करता है :

class Sealed final // ...

यह एक बेहतरीन तकनीक है। मैं इस पर एक ब्लॉग प्रविष्टि लिखूंगा।

1
प्रश्न: यदि हमने वर्चुअल इनहेरिटेंस का उपयोग नहीं किया है, तो FailsToDerive संकलन करेगा। सही बात?

4
+1। @ साशा: सही, वर्चुअल इनहेरिटेंस की जरूरत है क्योंकि सबसे व्युत्पन्न वर्ग हमेशा सभी वस्तुतः विरासत वाले वर्ग के कंस्ट्रक्टरों को सीधे कॉल करता है, जो सादे विरासत के साथ ऐसा नहीं है।
j_random_hacker

5
यह सामान्य बनाया जा सकता है, बिना कस्टम क्लाससेलर बनाए हर वर्ग के लिए जिसे आप सील करना चाहते हैं! इसे देखें: वर्ग ClassSealer {संरक्षित: ClassSealer () {}}; बस इतना ही।

बहुत शांत! BTW मैंने CRTP का उपयोग करने के बारे में आपकी पहले की टिप्पणी (अब हटा दी गई) देखी: मुझे लगता है कि वास्तव में काम करना चाहिए, यह टेम्पलेट दोस्तों के लिए सिंटैक्स प्राप्त करने के लिए बस मुश्किल है। लेकिन किसी भी मामले में आपका गैर-टेम्पलेट समाधान बहुत अधिक भयानक है :)
j_random_hacker

138

मैं इसका हर समय उपयोग करता हूं। मेरे सिर के ऊपर से कुछ उदाहरण:

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

एक विशिष्ट उदाहरण STL कंटेनर से निजी रूप से प्राप्त हो रहा है:

class MyVector : private vector<int>
{
public:
    // Using declarations expose the few functions my clients need 
    // without a load of forwarding functions. 
    using vector<int>::push_back;
    // etc...  
};
  • एडेप्टर पैटर्न को लागू करते समय, एडेप्टेड क्लास से निजी तौर पर विरासत में मिला एक संलग्न उदाहरण के लिए आगे होने से बचाता है।
  • निजी इंटरफ़ेस लागू करने के लिए। यह अक्सर ऑब्जर्वर पैटर्न के साथ आता है। आमतौर पर मेरा ऑब्जर्वर वर्ग, मायक्लास कहता है, कुछ विषय के साथ खुद को सदस्यता देता है । फिर, केवल MyClass को MyClass -> ऑब्जर्वर रूपांतरण करना होगा। बाकी सिस्टम को इसके बारे में जानने की आवश्यकता नहीं है, इसलिए निजी विरासत को इंगित किया जाता है।

4
@ कृष्ण: वास्तव में, मुझे ऐसा नहीं लगता। यहां केवल एक ही कारण है: आलस्य, आखिरी के अलावा, जो चारों ओर काम करने के लिए मुश्किल होगा।
मैथ्यू एम।

11
इतना आलस्य नहीं (जब तक आप अच्छे तरीके से इसका मतलब नहीं)। यह उन कार्यों के नए अधिभार के निर्माण की अनुमति देता है जो बिना किसी अतिरिक्त कार्य के उजागर हुए हैं। यदि C ++ 1x में वे 3 नए अधिभार जोड़ते हैं push_back, MyVectorतो उन्हें मुफ्त में मिलता है।
डेविड स्टोन

@DavidStone, क्या आप इसे टेम्पलेट विधि से नहीं कर सकते?
जुलिएनजी

5
@Julien__: हाँ, आप लिख सकते हैं template<typename... Args> constexpr decltype(auto) f(Args && ... args) noexcept(noexcept(std::declval<Base &>().f(std::forward<Args>(args)...)) and std::is_nothrow_move_constructible<decltype(std::declval<Base &>().f(std::forward<Args>(args)...))>) { return m_base.f(std::forward<Args>(args)...); }या आप का उपयोग कर लिख सकते हैं Base::f;। आप की कार्यक्षमता और लचीलापन है कि निजी विरासत के सबसे चाहते हैं और एक तो usingबयान आप देता है, तो आप प्रत्येक कार्य के लिए है कि राक्षस है (और के बारे में भूल नहीं है constऔर volatileभार के!)।
डेविड स्टोन

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

31

निजी उत्तराधिकार का विहित उपयोग "संबंध के संदर्भ में लागू किया गया है (स्कॉट वर्कर्स के लिए धन्यवाद '' इस शब्द के लिए प्रभावी C ++ ')। दूसरे शब्दों में, विरासत वाले वर्ग के बाहरी इंटरफ़ेस का विरासत वाले वर्ग से कोई (दृश्य) संबंध नहीं है, लेकिन यह अपनी कार्यक्षमता को लागू करने के लिए आंतरिक रूप से इसका उपयोग करता है।


6
इस मामले में इसका उपयोग करने के कारणों में से एक का उल्लेख करने योग्य हो सकता है: यह खाली बेस क्लास ऑप्टिमाइज़ेशन को करने की अनुमति देता है, जो तब होगा जब क्लास बेस क्लास के बजाय सदस्य था।
जलफ

2
इसका मुख्य उपयोग अंतरिक्ष की खपत को कम करना है जहां यह वास्तव में मायने रखता है, उदाहरण के लिए नीति नियंत्रित स्ट्रिंग कक्षाओं में या संपीड़ित जोड़े में। वास्तव में, बढ़ावा :: संकुचित_पेयर संरक्षित विरासत का इस्तेमाल किया।
जोहान्स शहाब -

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

3
यह भी एक वर्ग को अजेय बनाने के लिए आसान है - बस निजी तौर पर एक खाली वर्ग से विरासत में मिला जो प्रतिलिपि योग्य नहीं है। अब आपको घोषणा करने के व्यस्त काम से नहीं गुजरना है बल्कि निजी कॉपी कंस्ट्रक्टर और असाइनमेंट ऑपरेटर को परिभाषित नहीं करना है। मेयर्स इस बारे में भी बात करते हैं।
माइकल बूर

मुझे यह एहसास नहीं था कि वास्तव में संरक्षित विरासत के बजाय निजी विरासत के बारे में है। हाँ, मुझे लगता है कि इसके लिए काफी आवेदन हैं। संरक्षित विरासत के लिए कई उदाहरणों के बारे में नहीं सोच सकते हैं: / लगता है कि यह केवल शायद ही कभी उपयोगी है।
जोहान्स स्काउब -

23

निजी विरासत का एक उपयोगी उपयोग तब होता है जब आपके पास एक वर्ग होता है जो एक इंटरफ़ेस को लागू करता है, जो तब किसी अन्य ऑब्जेक्ट के साथ पंजीकृत होता है। आप उस इंटरफ़ेस को निजी बनाते हैं ताकि कक्षा को खुद को पंजीकृत करना पड़े और केवल विशिष्ट वस्तु जो कि उसके साथ पंजीकृत हो, उन कार्यों का उपयोग कर सके।

उदाहरण के लिए:

class FooInterface
{
public:
    virtual void DoSomething() = 0;
};

class FooUser
{
public:
    bool RegisterFooInterface(FooInterface* aInterface);
};

class FooImplementer : private FooInterface
{
public:
    explicit FooImplementer(FooUser& aUser)
    {
        aUser.RegisterFooInterface(this);
    }
private:
    virtual void DoSomething() { ... }
};

इसलिए FooUser वर्ग FooInterface इंटरफ़ेस के माध्यम से FooImplementer के निजी तरीकों को कॉल कर सकता है, जबकि अन्य अन्य वर्ग नहीं कर सकते। यह विशिष्ट कॉलबैक से निपटने के लिए एक बढ़िया पैटर्न है जिसे इंटरफेस के रूप में परिभाषित किया गया है।


1
दरअसल, निजी विरासत निजी IS-A है।
जिज्ञासु

18

मुझे लगता है कि C ++ FAQ Lite से महत्वपूर्ण खंड है:

निजी विरासत के लिए एक वैध, दीर्घकालिक उपयोग तब होता है जब आप एक वर्ग फ्रेड का निर्माण करना चाहते हैं जो एक वर्ग विल्मा में कोड का उपयोग करता है, और वर्ग विल्मा से कोड को आपके नए वर्ग, फ्रेड से सदस्य कार्यों को आमंत्रित करने की आवश्यकता होती है। इस मामले में, फ्रेड विल्मा में गैर-आभासी कॉल करता है, और विल्मा (आमतौर पर शुद्ध वर्चुअल) अपने आप में कॉल करता है, जो कि फ्रेड द्वारा ओवरराइड किया जाता है। रचना के साथ ऐसा करना बहुत कठिन होगा।

यदि संदेह है, तो आपको निजी विरासत पर रचना पसंद करनी चाहिए।


4

मुझे यह इंटरफेस (अर्थात अमूर्त वर्ग) के लिए उपयोगी लगता है जो मुझे विरासत में मिला है जहाँ मैं इंटरफ़ेस को छूने के लिए अन्य कोड नहीं चाहता (केवल विरासत वर्ग)।

[एक उदाहरण में संपादित]

ऊपर से जुड़ा उदाहरण लें । कहते हुए की

[...] वर्ग विल्मा को आपके नए वर्ग, फ्रेड से सदस्य कार्यों को आमंत्रित करने की आवश्यकता है।

कहने का मतलब यह है कि विल्मा को फ्रेड की आवश्यकता है कि वह कुछ सदस्य कार्यों को करने में सक्षम हो, या, बल्कि यह कह रहा है कि विल्मा एक इंटरफ़ेस है । इसलिए, जैसा कि उदाहरण में बताया गया है

निजी विरासत बुराई नहीं है; यह सिर्फ बनाए रखने के लिए अधिक महंगा है, क्योंकि यह संभावना बढ़ जाती है कि कोई व्यक्ति ऐसा कुछ बदलेगा जो आपके कोड को तोड़ देगा।

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

[एक अन्य उदाहरण में संपादित]

यह पृष्ठ निजी इंटरफ़ेस (अभी तक किसी अन्य कोण से) पर चर्चा करता है।


वास्तव में उपयोगी नहीं लगता ... क्या आप एक उदाहरण पोस्ट कर सकते हैं

मुझे लगता है कि मैं देख रहा हूं कि आप कहां जा रहे हैं ... एक विशिष्ट उपयोग मामला यह हो सकता है कि विल्मा कुछ प्रकार की उपयोगिता वर्ग है जिसे फ्रेड में आभासी कार्यों को कॉल करने की आवश्यकता है, लेकिन अन्य वर्गों को यह जानने की आवश्यकता नहीं है कि फ्रेड को लागू किया गया है- विल्मा का। सही?
j_random_hacker

हाँ। मुझे यह समझ लेना चाहिए कि, मेरी समझ से, 'इंटरफ़ेस' शब्द का जावा में अधिक इस्तेमाल होता है। जब मैंने पहली बार इसके बारे में सुना तो मुझे लगा कि इसे बेहतर नाम दिया जा सकता है। चूंकि, इस उदाहरण में हमारे पास एक इंटरफ़ेस है जो किसी के साथ उस तरह से हस्तक्षेप नहीं करता है जैसे हम आम तौर पर शब्द के बारे में सोचते हैं।
बायस

@ निओस: हां मुझे लगता है कि आपका बयान "विल्मा एक इंटरफेस है" थोड़ा अस्पष्ट है, क्योंकि ज्यादातर लोग इसका मतलब यह मान लेंगे कि विल्मा एक ऐसा इंटरफ़ेस है जिसे फ्रेड दुनिया में आपूर्ति करने का इरादा रखते हैं , बजाय केवल विल्मा के साथ अनुबंध के।
j_random_hacker 6

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

2

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

class BigClass;

struct SomeCollection
{
    iterator begin();
    iterator end();
};

class BigClass : private SomeCollection
{
    friend struct SomeCollection;
    SomeCollection &GetThings() { return *this; }
};

फिर अगर SomeCollection को BigClass तक पहुंचने की आवश्यकता है, तो यह कर सकता है static_cast<BigClass *>(this)। अंतरिक्ष में अतिरिक्त डेटा सदस्य रखने की आवश्यकता नहीं है।


BigClassइस उदाहरण में आगे की घोषणा की कोई आवश्यकता नहीं है? मुझे यह दिलचस्प लगता है, लेकिन यह मेरे चेहरे में हैकिश चिल्लाता है।
थॉमस ईडिंग

2

मुझे निजी विरासत के लिए एक अच्छा आवेदन मिला, हालांकि इसका सीमित उपयोग है।

हल करने की समस्या

मान लीजिए आपको निम्नलिखित C API दिया गया है:

#ifdef __cplusplus
extern "C" {
#endif

    typedef struct
    {
        /* raw owning pointer, it's C after all */
        char const * name;

        /* more variables that need resources
         * ...
         */
    } Widget;

    Widget const * loadWidget();

    void freeWidget(Widget const * widget);

#ifdef __cplusplus
} // end of extern "C"
#endif

अब आपका काम C ++ का उपयोग करके इस एपीआई को लागू करना है।

सी-ईश दृष्टिकोण

बेशक हम सी-ईश कार्यान्वयन शैली चुन सकते हैं जैसे:

Widget const * loadWidget()
{
    auto result = std::make_unique<Widget>();
    result->name = strdup("The Widget name");
    // More similar assignments here
    return result.release();
}

void freeWidget(Widget const * const widget)
{
    free(result->name);
    // More similar manual freeing of resources
    delete widget;
}

लेकिन इसके कई नुकसान हैं:

  • मैनुअल संसाधन (जैसे मेमोरी) प्रबंधन
  • structगलत को सेट करना आसान है
  • मुक्त करने के लिए संसाधनों को मुक्त करना भूलना आसान है struct
  • यह सी-ईश है

C ++ दृष्टिकोण

हमें C ++ का उपयोग करने की अनुमति है, इसलिए इसकी पूर्ण शक्तियों का उपयोग क्यों नहीं किया जाता है?

पेश है स्वचालित संसाधन प्रबंधन

उपरोक्त समस्याएं मूल रूप से मैनुअल संसाधन प्रबंधन से जुड़ी हैं। समाधान जो मन में आता है वह प्रत्येक चर के लिए Widgetव्युत्पन्न वर्ग में संसाधन प्रबंधन उदाहरण से विरासत में मिला है WidgetImpl:

class WidgetImpl : public Widget
{
public:
    // Added bonus, Widget's members get default initialized
    WidgetImpl()
        : Widget()
    {}

    void setName(std::string newName)
    {
        m_nameResource = std::move(newName);
        name = m_nameResource.c_str();
    }

    // More similar setters to follow

private:
    std::string m_nameResource;
};

यह निम्नलिखित के लिए कार्यान्वयन को सरल बनाता है:

Widget const * loadWidget()
{
    auto result = std::make_unique<WidgetImpl>();
    result->setName("The Widget name");
    // More similar setters here
    return result.release();
}

void freeWidget(Widget const * const widget)
{
    // No virtual destructor in the base class, thus static_cast must be used
    delete static_cast<WidgetImpl const *>(widget);
}

इस तरह हमने उपरोक्त सभी समस्याओं को दूर किया। लेकिन एक ग्राहक अभी भी बसने के बारे में भूल सकता है WidgetImplऔर उसे असाइन कर सकता हैWidget सदस्यों को सीधे सकता है।

निजी विरासत मंच में प्रवेश करती है

उन Widgetसदस्यों को एनकैप्सुलेट करने के लिए हम निजी विरासत का उपयोग करते हैं। अफसोस की बात यह है कि अब हमें दोनों वर्गों के बीच दो अतिरिक्त कार्य करने होंगे:

class WidgetImpl : private Widget
{
public:
    WidgetImpl()
        : Widget()
    {}

    void setName(std::string newName)
    {
        m_nameResource = std::move(newName);
        name = m_nameResource.c_str();
    }

    // More similar setters to follow

    Widget const * toWidget() const
    {
        return static_cast<Widget const *>(this);
    }

    static void deleteWidget(Widget const * const widget)
    {
        delete static_cast<WidgetImpl const *>(widget);
    }

private:
    std::string m_nameResource;
};

यह निम्नलिखित अनुकूलन आवश्यक बनाता है:

Widget const * loadWidget()
{
    auto widgetImpl = std::make_unique<WidgetImpl>();
    widgetImpl->setName("The Widget name");
    // More similar setters here
    auto const result = widgetImpl->toWidget();
    widgetImpl.release();
    return result;
}

void freeWidget(Widget const * const widget)
{
    WidgetImpl::deleteWidget(widget);
}

यह समाधान सभी समस्याओं को हल करता है। कोई मैनुअल मेमोरी प्रबंधन और Widgetअच्छी तरह से समझाया गया है ताकिWidgetImpl कोई भी सार्वजनिक डेटा सदस्य न हो। गलत तरीके से उपयोग करने के लिए कार्यान्वयन को सही और कठिन (असंभव?) करना आसान बनाता है।

कोड स्निपेट कोलिरु पर एक संकलन उदाहरण है


1

यदि व्युत्पन्न वर्ग - को कोड का पुन: उपयोग करने की आवश्यकता है और - आप बेस क्लास को बदल नहीं सकते हैं और - एक लॉक के तहत आधार के सदस्यों का उपयोग करके अपने तरीकों की रक्षा कर रहे हैं।

तब आपको निजी विरासत का उपयोग करना चाहिए, अन्यथा आपको इस व्युत्पन्न वर्ग के माध्यम से निर्यात किए गए अनलॉक किए गए आधार तरीकों का खतरा है।


1

कभी-कभी यह एकत्रीकरण का विकल्प हो सकता है , उदाहरण के लिए यदि आप एकत्रीकरण चाहते हैं, लेकिन एकत्र करने योग्य इकाई के परिवर्तनित व्यवहार (आभासी कार्यों को ओवरराइड करना) के साथ।

लेकिन आप सही कह रहे हैं, वास्तविक दुनिया से इसके कई उदाहरण नहीं हैं।


0

जब संबंध "निजी नहीं है" का उपयोग किया जाना है, लेकिन नई कक्षा को "मौजूदा वर्ग की अवधि में लागू किया जा सकता है" या "मौजूदा वर्ग की तरह नए वर्ग" का काम किया जा सकता है।

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

एक वर्ग "है-एक" आयत (गणितीय) लेकिन एक वर्ग एक आयत (व्यवहार) नहीं है। नतीजतन, "is-a," के बजाय हम "काम-जैसा-ए" कहना पसंद करते हैं (या, यदि आप पसंद करते हैं, तो विवरण को गलतफहमी का खतरा कम करने के लिए "प्रयोग करने योग्य-जैसा")।


0

एक वर्ग एक आक्रमणकारी रखता है। निर्माणकर्ता द्वारा अपरिवर्तनीय की स्थापना की जाती है। हालाँकि, कई स्थितियों में ऑब्जेक्ट के प्रतिनिधित्व की स्थिति (जो आप नेटवर्क पर संचारित कर सकते हैं या फ़ाइल को सहेज सकते हैं - डीटीओ यदि आप चाहें तो) को देखने के लिए उपयोगी है। एग्रीगेटटाइप के संदर्भ में REST सबसे अच्छा किया जाता है। यदि आप सही हैं तो यह विशेष रूप से सच है। विचार करें:

struct QuadraticEquationState {
   const double a;
   const double b;
   const double c;

   // named ctors so aggregate construction is available,
   // which is the default usage pattern
   // add your favourite ctors - throwing, try, cps
   static QuadraticEquationState read(std::istream& is);
   static std::optional<QuadraticEquationState> try_read(std::istream& is);

   template<typename Then, typename Else>
   static std::common_type<
             decltype(std::declval<Then>()(std::declval<QuadraticEquationState>()),
             decltype(std::declval<Else>()())>::type // this is just then(qes) or els(qes)
   if_read(std::istream& is, Then then, Else els);
};

// this works with QuadraticEquation as well by default
std::ostream& operator<<(std::ostream& os, const QuadraticEquationState& qes);

// no operator>> as we're const correct.
// we _might_ (not necessarily want) operator>> for optional<qes>
std::istream& operator>>(std::istream& is, std::optional<QuadraticEquationState>);

struct QuadraticEquationCache {
   mutable std::optional<double> determinant_cache;
   mutable std::optional<double> x1_cache;
   mutable std::optional<double> x2_cache;
   mutable std::optional<double> sum_of_x12_cache;
};

class QuadraticEquation : public QuadraticEquationState, // private if base is non-const
                          private QuadraticEquationCache {
public:
   QuadraticEquation(QuadraticEquationState); // in general, might throw
   QuadraticEquation(const double a, const double b, const double c);
   QuadraticEquation(const std::string& str);
   QuadraticEquation(const ExpressionTree& str); // might throw
}

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

निजी विरासत लगभग हमेशा एक सदस्य द्वारा तैयार की जा सकती है (यदि आवश्यक हो तो आधार के संदर्भ में भंडारण)। यह हमेशा उस तरह से मॉडल करने के लिए इसके लायक नहीं है; कभी-कभी विरासत सबसे कुशल प्रतिनिधित्व है।


0

यदि आपको std::ostreamकुछ छोटे परिवर्तनों (जैसे इस प्रश्न में ) की आवश्यकता है, तो आपको इसकी आवश्यकता हो सकती है

  1. एक वर्ग बनाएं, MyStreambufजो std::streambufवहां से आए और वहां बदलाव लागू करे
  2. एक ऐसा वर्ग बनाएं, MyOStreamजो इससे उत्पन्न std::ostreamभी हो और इसके उदाहरण का प्रबंधन MyStreambufऔर निर्माण करने वाले के लिए सूचक को पास करेstd::ostream

पहला विचार वर्ग में MyStreamडेटा सदस्य के रूप में उदाहरण जोड़ना हो सकता है MyOStream:

class MyOStream : public std::ostream
{
public:
    MyOStream()
        : std::basic_ostream{ &m_buf }
        , m_buf{}
    {}

private:
    MyStreambuf m_buf;
};

लेकिन आधार वर्ग किसी भी डेटा के सदस्यों से पहले निर्माण कर रहे हैं ताकि आप एक नहीं अभी तक का निर्माण करने के लिए एक सूचक गुजर रहे हैं std::streambufउदाहरण के लिए std::ostreamजो अपरिभाषित व्यवहार है।

उक्त प्रश्न के बेन के उत्तर में समाधान प्रस्तावित है , बस पहले स्ट्रीम बफर से वारिस करें, फिर स्ट्रीम से और फिर स्ट्रीम को इनिशियलाइज़ करें this:

class MyOStream : public MyStreamBuf, public std::ostream
{
public:
    MyOStream()
        : MyStreamBuf{}
        , basic_ostream{ this }
    {}
};

हालाँकि परिणामी वर्ग को एक std::streambufउदाहरण के रूप में भी इस्तेमाल किया जा सकता है जो आमतौर पर अवांछित है। निजी विरासत पर स्विच करना इस समस्या को हल करता है:

class MyOStream : private MyStreamBuf, public std::ostream
{
public:
    MyOStream()
        : MyStreamBuf{}
        , basic_ostream{ this }
    {}
};

-1

सिर्फ इसलिए कि C ++ में एक सुविधा है, इसका मतलब यह नहीं है कि यह उपयोगी है या इसका उपयोग किया जाना चाहिए।

मैं कहूंगा कि आपको इसका इस्तेमाल बिल्कुल नहीं करना चाहिए।

यदि आप इसे वैसे भी उपयोग कर रहे हैं, तो ठीक है, आप मूल रूप से इनकैप्सुलेशन का उल्लंघन कर रहे हैं, और सामंजस्य कम कर रहे हैं। आप डेटा को एक कक्षा में रख रहे हैं, और उन तरीकों को जोड़ रहे हैं जो डेटा को दूसरे में जोड़ते हैं।

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


क्या आप व्यंग्यात्मक हो रहे हैं? सब मेरे पास है -1! वैसे भी -100 वोट मिलने पर भी मैं इसे नहीं
हटाऊंगा

9
" आप मूल रूप से इनकैप्सुलेशन का उल्लंघन कर रहे हैं " क्या आप एक उदाहरण दे सकते हैं?
जिज्ञासु

1
एक कक्षा में डेटा और दूसरे ध्वनियों में व्यवहार लचीलापन में वृद्धि की तरह लगता है, क्योंकि एक से अधिक व्यवहार वर्ग और ग्राहक हो सकते हैं और वे जो चाहते हैं उसे संतुष्ट करने के लिए चुनते हैं
makar
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.