C ++ में "सुपर" का उपयोग करना


203

कोडिंग की मेरी शैली में निम्नलिखित मुहावरे शामिल हैं:

class Derived : public Base
{
   public :
      typedef Base super; // note that it could be hidden in
                          // protected/private section, instead

      // Etc.
} ;

यह मुझे "सुपर" को बेस के लिए एक उपनाम के रूप में उपयोग करने में सक्षम बनाता है, उदाहरण के लिए, निर्माणकर्ताओं में:

Derived(int i, int j)
   : super(i), J(j)
{
}

या तब भी जब इसके अधिरोहित संस्करण के अंदर बेस क्लास से विधि को कॉल किया जाता है:

void Derived::foo()
{
   super::foo() ;

   // ... And then, do something else
}

यह भी जंजीर हो सकता है (मैं अभी भी उस के लिए उपयोग खोजने के लिए है, हालांकि):

class DerivedDerived : public Derived
{
   public :
      typedef Derived super; // note that it could be hidden in
                             // protected/private section, instead

      // Etc.
} ;

void DerivedDerived::bar()
{
   super::bar() ; // will call Derived::bar
   super::super::bar ; // will call Base::bar

   // ... And then, do something else
}

वैसे भी, मुझे "टाइपफेड सुपर" का उपयोग बहुत उपयोगी लगता है, उदाहरण के लिए, जब बेस या तो क्रिया है और / या अस्थायी है।

तथ्य यह है कि सुपर को जावा में लागू किया गया है, साथ ही साथ सी # (जहां इसे "आधार" कहा जाता है, जब तक कि मैं गलत नहीं हूं)। लेकिन C ++ में इस कीवर्ड का अभाव है।

तो, मेरे सवाल:

  • क्या यह आपके द्वारा काम किए गए कोड में टाइप किए गए सुपर आम / दुर्लभ / कभी नहीं देखा गया है?
  • क्या यह टाइपडिफ सुपर ओके का उपयोग है (अर्थात क्या आप इसका उपयोग नहीं करने के लिए मजबूत या इतने मजबूत कारण देखते हैं)?
  • "सुपर" एक अच्छी बात होनी चाहिए, क्या इसे सी ++ में कुछ हद तक मानकीकृत किया जाना चाहिए, या क्या यह पहले से ही एक टाइपराइफ के माध्यम से उपयोग किया जाता है?

संपादित करें: रॉडी ने इस तथ्य का उल्लेख किया कि टाइपडेफ निजी होना चाहिए। इसका मतलब यह है कि कोई भी व्युत्पन्न वर्ग इसे पुन: घोषित किए बिना इसका उपयोग करने में सक्षम नहीं होगा। लेकिन मुझे लगता है कि यह सुपर को भी रोक देगा: सुपर चेनिंग (लेकिन जो इसके लिए रो रहा है?)।

संपादित करें 2: अब, "सुपर" का बड़े पैमाने पर उपयोग करने के कुछ महीने बाद, मैं पूरी ईमानदारी से रोडी के दृष्टिकोण से सहमत हूं: "सुपर" निजी होना चाहिए। मैं उनके उत्तर को दो बार बढ़ाऊंगा, लेकिन मुझे लगता है कि मैं नहीं कर सकता।


बहुत बढ़िया! ठीक वही जो मेरे द्वारा खोजा जा रहा था। मुझे नहीं लगता कि मुझे अब तक इस तकनीक का उपयोग करने की आवश्यकता है। मेरे क्रॉस-प्लेटफ़ॉर्म कोड के लिए उत्कृष्ट समाधान।
एलनकेली

6
मेरे लिए superजैसा दिखता है Javaऔर यह कुछ भी बुरा नहीं है, लेकिन ... लेकिन C++कई विरासत का समर्थन करता है।
ST3

2
@ user2623967: सही है। साधारण विरासत के मामले में, एक "सुपर" पर्याप्त है। अब, यदि आपके पास कई विरासत हैं, तो "सुपरए", "सुपरबी", आदि एक अच्छा समाधान है: आप एक कार्यान्वयन या किसी अन्य से विधि को कॉल करना चाहते हैं, इसलिए आपको यह बताना होगा कि आप क्या कार्यान्वयन चाहते हैं। "सुपर" की तरहMyFirstBase<MyString, MyStruct<MyData, MyValue>>
टाइपडैफ

ध्यान दें कि जब एक टेम्पलेट से विरासत में मिला, तो आपको इसे संदर्भित करते समय टेम्पलेट तर्क को शामिल करने की आवश्यकता नहीं है। उदाहरण के लिए: template <class baz> struct Foo {...void bar() {...} ...}; struct Foo2: Foo<AnnoyinglyLongListOfArguments> { void bar2() { ... Foo::bar(); ...} };इसने मेरे साथ gcc 9.1 --std = c ++ 1y (c ++ 14) काम किया।
थानासिस पापाउटसिडकिस

1
... उम, सुधार। यह किसी भी सी + + मानक में काम करने के लिए लगता है, न कि केवल 14.
थानिसिस पापुटीसाडिसिस

जवाबों:


151

Bjarne Stroustrup में उल्लेख है डिजाइन और सी के विकास ++ कि superजैसे कीवर्ड आईएसओ सी ++ मानक समिति द्वारा पहली बार सी ++ मानकीकृत किया गया माना जाता था।

डैग ब्रुक ने इस विस्तार का प्रस्ताव दिया, बेस क्लास को "विरासत में मिला"। प्रस्ताव में कई विरासत के मुद्दे का उल्लेख किया गया है, और अस्पष्ट उपयोगों को चिह्नित किया जाएगा। यहां तक ​​कि स्ट्रॉस्ट्रुप भी आश्वस्त था।

चर्चा के बाद, Dag Bruck (हाँ, प्रस्ताव बनाने वाले एक ही व्यक्ति) ने लिखा कि यह प्रस्ताव लागू करने योग्य, तकनीकी रूप से ध्वनि और बड़ी खामियों से मुक्त था, और कई विरासतों को संभाला था। दूसरी ओर, हिरन के लिए पर्याप्त धमाका नहीं था, और समिति को एक कांटेदार समस्या को संभालना चाहिए।

माइकल टिएमैन देर से पहुंचे, और फिर दिखाया कि इस पोस्ट में उसी तकनीक का उपयोग किया गया है, जिसके बारे में पूछा गया था।

तो, नहीं, यह शायद कभी मानकीकृत नहीं होगा।

यदि आपके पास एक प्रति नहीं है, तो डिजाइन और विकास अच्छी तरह से कवर मूल्य के लायक है। प्रयुक्त प्रतियाँ लगभग $ 10 के लिए हो सकती हैं।


5
डी एंड ई वास्तव में एक अच्छी किताब है। लेकिन ऐसा लगता है कि मुझे इसे फिर से पढ़ने की आवश्यकता होगी - मुझे या तो कहानी याद नहीं है।
माइकल बूर

2
मुझे तीन विशेषताएं याद हैं जिन्हें डी एंड ई में स्वीकार नहीं किया गया था। यह कहानी को खोजने के लिए सूचकांक में पहला ("माइकल टिएमैन" देखो), दो सप्ताह का नियम दूसरा है (सूचकांक में "दो सप्ताह का नियम देखें"), और तीसरे का नाम पैरामीटर (ऊपर देखो) था तर्कों का नाम "सूचकांक में"।
मैक्स लाइबबर्ट

12
typedefतकनीक में एक प्रमुख दोष है : यह DRY का सम्मान नहीं करता है। कक्षाओं को घोषित करने के लिए बदसूरत मैक्रोज़ का उपयोग करने का एकमात्र तरीका होगा। जब आप वारिस होते हैं, तो आधार एक लंबे बहु पैरामीटर टेम्पलेट वर्ग, या इससे भी बदतर हो सकता है। (उदाहरण के लिए मल्टी क्लास) आपको दूसरी बार सभी को फिर से लिखना होगा। अंत में, मुझे खाका के साथ एक बड़ा मुद्दा दिखाई देता है जिसमें टेम्पलेट वर्ग तर्क होते हैं। इस मामले में सुपर एक टेम्प्लेट है (और न कि किसी टेम्प्लेट का एक इंस्टेंस)। जिसे टाइप किया नहीं जा सकता। यहां तक ​​कि C ++ 11 में आपको usingइस मामले की आवश्यकता है ।
v.oddou

105

मैंने हमेशा सुपर के बजाय "विरासत में मिली" का उपयोग किया है। (संभवतः डेल्फी पृष्ठभूमि के कारण), और मैं हमेशा इसे निजी बनाता हूं , इस समस्या से बचने के लिए जब 'विरासत' को गलत तरीके से एक वर्ग से छोड़ दिया जाता है लेकिन एक उपवर्ग इसका उपयोग करने की कोशिश करता है।

class MyClass : public MyBase
{
private:  // Prevents erroneous use by other classes.
  typedef MyBase inherited;
...

नई कक्षाएँ बनाने के लिए मेरे मानक 'कोड टेम्प्लेट' में टाइपडिफ़ शामिल है, इसलिए मेरे पास गलती से इसे छोड़ने का अवसर कम है।

मुझे नहीं लगता कि जंजीर "सुपर :: सुपर" सुझाव एक अच्छा विचार है- यदि आप ऐसा कर रहे हैं, तो आप शायद एक विशेष पदानुक्रम के लिए बहुत मुश्किल से बंधे हैं, और इसे बदलने से संभवतः सामान बुरी तरह से टूट जाएगा।


2
सुपर :: सुपर का पीछा करने के लिए, जैसा कि मैंने प्रश्न में उल्लेख किया है, मुझे अभी भी उस के लिए एक दिलचस्प उपयोग करना है। अभी के लिए, मैं इसे केवल एक हैक के रूप में देखता हूं, लेकिन यह केवल जावा (जहां आप "सुपर" को चेन नहीं कर सकते) के अंतर के लिए उल्लेख करने योग्य था।
पियरसबल

4
कुछ महीनों के बाद, मुझे आपके दृष्टिकोण में बदल दिया गया (और मुझे भूल गए "सुपर" के कारण एक बग था, जैसे आपने उल्लेख किया था ...)। आप अपने उत्तर में बिलकुल सही हैं, जिसमें चैनिंग भी शामिल है, मुझे लगता है। ^ _ ^ ...
पियरसबल

अभिभावक वर्ग विधियों को कॉल करने के लिए मैं इसका उपयोग कैसे करूं?
mLstudent33

क्या इसका मतलब है कि मुझे सभी बेस क्लास विधियों को virtualयहाँ घोषित करना होगा : martinbroadhurst.com/typedef-super.html
mLstudent33

36

इसके साथ एक समस्या यह है कि यदि आप व्युत्पन्न वर्गों के लिए सुपर (पुनः) को परिभाषित करते हैं, तो सुपर :: कुछ भी कॉल ठीक संकलित करेगा, लेकिन संभवतः वांछित फ़ंक्शन को कॉल नहीं करेगा।

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

class Base
{
public:  virtual void foo() { ... }
};

class Derived: public Base
{
public:
    typedef Base super;
    virtual void foo()
    {
        super::foo();   // call superclass implementation

        // do other stuff
        ...
    }
};

class DerivedAgain: public Derived
{
public:
    virtual void foo()
    {
        // Call superclass function
        super::foo();    // oops, calls Base::foo() rather than Derived::foo()

        ...
    }
};

(जैसा कि इस उत्तर में टिप्पणियों में मार्टिन यॉर्क द्वारा कहा गया है, इस समस्या को सार्वजनिक या संरक्षित करने के बजाय टंकण को ​​निजी बनाकर समाप्त किया जा सकता है।)


टिप्पणी के लिए धन्यवाद। यह दुष्प्रभाव मेरे नोटिस से बच गया था। हालांकि यह शायद कंस्ट्रक्टर के उपयोग के लिए संकलित नहीं होगा, हर जगह, मुझे लगता है, बग वहाँ होगा।
22

5
हालांकि, निजी टाइपिडिफ मूल पोस्ट में उल्लिखित जंजीर के उपयोग को रोक देगा।
An ०

1
इस सटीक बग में भागना मुझे इस प्रश्न की ओर ले जाता है :(
स्टीव वर्म्यूलेन

तो super1बेस के लिए और super2व्युत्पन्न में उपयोग करें ? DerivedAgain दोनों का उपयोग कर सकते हैं?
mLstudent33

20

FWIW Microsoft ने अपने संकलक में __super के लिए एक एक्सटेंशन जोड़ा है ।


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

8
मैं एक विंडोज़ ऐप पर काम कर रहा हूं, और __super एक्सटेंशन को प्यार करता हूं। यह मुझे दुखद है कि मानकों की समिति ने यहां बताई गई टाइप्डिफ ट्रिक के पक्ष में इसे खारिज कर दिया, क्योंकि जब यह टाइपडीफ ट्रिक अच्छी होती है, तो आपको संकलक कीवर्ड की तुलना में अधिक रखरखाव की आवश्यकता होती है, जब आप वंशानुक्रम पदानुक्रम बदलते हैं, और अपनी मूल योग्यता को सही ढंग से संभालते हैं (दो की आवश्यकता के बिना) सुपर 1 और सुपर 2 जैसे टाइपेडिफ्स)। संक्षेप में, मैं अन्य टिप्पणीकार से सहमत हूं कि एमएस एक्सटेंशन बहुत उपयोगी है, और विज़ुअल स्टूडियो का उपयोग करने वाले किसी व्यक्ति को विशेष रूप से इसका उपयोग करने पर जोर देना चाहिए।
ब्रायन

15

सुपर (या विरासत में मिली) बहुत अच्छी चीज है क्योंकि यदि आपको बेस और डेरेव्ड के बीच एक और विरासत की परत को चिपकाने की आवश्यकता है, तो आपको केवल दो चीजों को बदलना होगा: 1. "क्लास बेस: फू" और 2. टाइपडिफ।

यदि मुझे सही तरीके से याद है, तो C ++ मानक समिति इसके लिए एक कीवर्ड जोड़ने पर विचार कर रही थी ... जब तक कि माइकल टिएमैन ने यह नहीं बताया कि यह टाइप्डिफ ट्रिक काम करती है।

के रूप में कई विरासत के लिए, क्योंकि यह प्रोग्रामर नियंत्रण के तहत आप कर सकते हैं जो आप चाहते हैं: शायद सुपर 1 और सुपर 2, या जो भी हो।


13

मुझे अभी वैकल्पिक वैकल्पिक हल मिला है। मुझे आज जो बिटकॉइन एप्रोच है उसके साथ एक बड़ी समस्या है:

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

इसलिए मैं एक बहुत ही सरल टेम्पलेट का उपयोग करके बेहतर समाधान के साथ आया।

template <class C>
struct MakeAlias : C
{ 
    typedef C BaseAlias;
};

तो अब, के बजाय

class Derived : public Base
{
private:
    typedef Base Super;
};

आपके पास

class Derived : public MakeAlias<Base>
{
    // Can refer to Base as BaseAlias here
};

इस मामले में, BaseAliasनिजी नहीं है और मैंने एक प्रकार के नाम का चयन करके लापरवाह उपयोग से बचने की कोशिश की है जो अन्य डेवलपर्स को सचेत करे।


4
publicउर्फ, एक नकारात्मक पक्ष यह है के रूप में आप बग में उल्लेख किया है के लिए खुला रहे हैं रोडी के और क्रिस्टोफ़र के (जैसे आप (गलती से) से निकाले जाते हैं कर सकते हैं जवाब Derivedके बजाय MakeAlias<Derived>)
अलेक्जेंडर Malakhov

3
आपकी शुरुआती सूचियों में आपके पास बेस क्लास कंस्ट्रक्टर्स तक पहुंच नहीं है। (यह सी ++ 11 में इनहेरिट किए गए कंस्ट्रक्टरों का उपयोग करके MakeAliasया पूर्ण अग्रेषण में किया जा सकता है, लेकिन इससे आपको कंस्ट्रक्टरों को सीधे संदर्भित करने की MakeAlias<BaseAlias>बजाए आवश्यकता होती है C।)
एडम एच। पीटरसन

12

मुझे यह देखकर याद नहीं है, लेकिन पहली नज़र में मुझे यह पसंद आया। जैसा कि फेरुचियो नोट करता है, यह एमआई के सामने अच्छी तरह से काम नहीं करता है, लेकिन एमआई नियम की तुलना में अधिक अपवाद है और ऐसा कुछ भी नहीं है जो कहता है कि कुछ उपयोगी होने के लिए हर जगह उपयोग करने की आवश्यकता है।


6
केवल वाक्यांश के लिए अपवोट करें, "ऐसा कुछ भी नहीं है जो कहता है कि कुछ उपयोगी होने के लिए हर जगह उपयोगी होने की आवश्यकता है।"
टंकटालस

1
माना। यह उपयोगी है। मैं सिर्फ बूस्ट टाइप ट्रिट्स लाइब्रेरी को देख रहा था कि क्या कोई टेम्प्लेट के माध्यम से टाइपफ़ेड उत्पन्न करने का कोई तरीका है। दुर्भाग्य से, ऐसा नहीं लगता कि आप कर सकते हैं।
फेर्रुकियो

9

मैंने इस मुहावरे को कई कोड में नियोजित देखा है और मुझे पूरा यकीन है कि मैंने इसे बूस्ट के पुस्तकालयों में भी देखा है। हालाँकि, जहाँ तक मुझे याद है कि इसके बजाय सबसे आम नाम base(या Base) है super

खासतौर पर टेम्प्लेट क्लासेस के साथ काम करने पर यह मुहावरा उपयोगी है। एक उदाहरण के रूप में, निम्न वर्ग ( वास्तविक परियोजना से ) पर विचार करें:

template <typename TText, typename TSpec>
class Finder<Index<TText, PizzaChili<TSpec> >, PizzaChiliFinder>
    : public Finder<Index<TText, PizzaChili<TSpec> >, Default>
{
    typedef Finder<Index<TText, PizzaChili<TSpec> >, Default> TBase;
    // …
}

मजाकिया नामों का बुरा मत मानना। यहाँ महत्वपूर्ण बिंदु यह है कि विरासत श्रृंखला संकलन-समय के बहुरूपता को प्राप्त करने के लिए प्रकार के तर्कों का उपयोग करती है। दुर्भाग्य से, इन टेम्प्लेट्स का घोंसला स्तर काफी अधिक हो जाता है। इसलिए, पठनीयता और स्थिरता के लिए संक्षिप्त रूप महत्वपूर्ण है।


मैंने "सुपर" का उपयोग हाल ही में उसी कारण से किया था। सार्वजनिक विरासत भी अन्यथा को संभालने के लिए दर्दनाक था ... :-) ...
paercebal

4

मैंने अक्सर इसका इस्तेमाल करते हुए देखा है, कभी-कभी सुपर_ट के रूप में, जब आधार एक जटिल टेम्पलेट प्रकार होता है ( boost::iterator_adaptorउदाहरण के लिए, यह होता है)


1
आप सही हे। मुझे यहां संदर्भ मिला। boost.org/doc/libs/1_36_0/libs/iterator/doc/…
22

4

क्या यह आपके द्वारा काम किए गए कोड में टाइप किए गए सुपर आम / दुर्लभ / कभी नहीं देखा गया है?

मैंने कभी भी C ++ कोड में इस विशेष पैटर्न को नहीं देखा, जिसके साथ मैं काम करता हूं, लेकिन इसका मतलब यह नहीं है कि यह वहां नहीं है।

क्या यह टाइपडिफ सुपर ओके का उपयोग है (अर्थात क्या आप इसका उपयोग नहीं करने के लिए मजबूत या इतने मजबूत कारण देखते हैं)?

यह कई वंशानुक्रम (सफाई से, वैसे भी) के लिए अनुमति नहीं देता है।

"सुपर" एक अच्छी बात होनी चाहिए, क्या इसे सी ++ में कुछ हद तक मानकीकृत किया जाना चाहिए, या क्या यह पहले से ही एक टाइपराइफ के माध्यम से उपयोग किया जाता है?

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

ओह, और FYI करें: C ++ / CLI इस अवधारणा का समर्थन "__super" कीवर्ड के रूप में करता है। कृपया ध्यान दें, हालांकि, C ++ / CLI एकाधिक वंशानुक्रम का समर्थन नहीं करता है।


4
एक काउंटर-पॉइंट के रूप में, पर्ल के पास कई विरासत और सुपर दोनों हैं । कुछ भ्रम है, लेकिन कुछ को खोजने के लिए वीएम जिस एल्गोरिदम से गुजरता है वह स्पष्ट रूप से प्रलेखित है। उस ने कहा, मैं शायद ही कभी एमआई का उपयोग करता हूं, जहां कई आधार कक्षाएं उन्हीं तरीकों की पेशकश करती हैं, जहां वैसे भी भ्रम पैदा हो सकता है।
टंकटालस

1
Microsoft Visual Studio C ++ के लिए __super कीवर्ड को लागू करता है या नहीं यह CLI के लिए है। यदि विरासत में मिली कक्षाओं में से केवल एक ही सही हस्ताक्षर की विधि प्रदान करता है (सबसे सामान्य मामला, जैसा कि टंकाल्टस द्वारा उल्लेख किया गया है) तो यह केवल एकमात्र वैध विकल्प चुनता है। यदि दो या अधिक विरासत वाली कक्षाएं एक फ़ंक्शन मिलान प्रदान करती हैं, तो यह काम नहीं करता है और आपको स्पष्ट होना चाहिए।
ब्रायन

3

सुपरक्लास के लिए एक टाइफाइड का उपयोग करने का एक अतिरिक्त कारण यह है कि जब आप ऑब्जेक्ट की विरासत में जटिल टेम्पलेट का उपयोग कर रहे हैं।

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

template <typename T, size_t C, typename U>
class A
{ ... };

template <typename T>
class B : public A<T,99,T>
{ ... };

कक्षा बी में यह ए के लिए एक टंकण के लिए आदर्श होगा अन्यथा आप हर जगह इसे दोहराते हुए फंस जाएंगे, जो आप ए के सदस्यों को संदर्भित करना चाहते थे।

इन मामलों में यह कई उत्तराधिकारियों के साथ भी काम कर सकता है, लेकिन आपके पास 'सुपर' नाम का एक टाइफाइड नहीं होगा, इसे 'base_A_t' या ऐसा कुछ कहा जाएगा।

--jeffk ++


2

टर्बो पास्कल से दिन में वापस C ++ में माइग्रेट करने के बाद, मैंने टर्बो पास्कल "इनहेरिटेड" कीवर्ड के बराबर होने के लिए ऐसा किया, जो उसी तरह से काम करता है। हालाँकि, C ++ में कुछ वर्षों तक प्रोग्रामिंग करने के बाद मैंने इसे करना बंद कर दिया। मैंने पाया कि मुझे इसकी बहुत आवश्यकता नहीं थी।


1

मुझे नहीं पता कि यह दुर्लभ है या नहीं, लेकिन मैंने निश्चित रूप से एक ही काम किया है।

जैसा कि बताया गया है, भाषा के इस हिस्से को बनाने में कठिनाई तब होती है जब एक वर्ग कई उत्तराधिकार का उपयोग करता है।


1

मैं समय-समय पर इसका उपयोग करता हूं। बस जब मैं अपने आप को बेस क्लास टाइप का एक-दो बार टाइप करता हुआ पाऊँगा, तो मैं इसे आपके जैसे टाइपराइफ़ से बदल दूँगा।

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

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


1

मैं इसी सटीक समस्या को हल करने की कोशिश कर रहा था; मैंने कुछ विचारों के आसपास फेंक दिया, जैसे कि माता-पिता की मनमानी संख्या के लिए अनुमति देने के लिए वैरेडिक टेम्प्लेट और पैक विस्तार का उपयोग करना, लेकिन मुझे एहसास हुआ कि इसके परिणामस्वरूप 'सुपर 0' और 'सुपर 1' का कार्यान्वयन होगा। मैंने इसे रौंद दिया क्योंकि इससे शुरू होने की तुलना में यह मुश्किल से अधिक उपयोगी होगा।

मेरे समाधान में एक सहायक वर्ग शामिल है PrimaryParentऔर इसे इस प्रकार लागू किया गया है:

template<typename BaseClass>
class PrimaryParent : virtual public BaseClass
{
protected:
    using super = BaseClass;
public:
    template<typename ...ArgTypes>
    PrimaryParent<BaseClass>(ArgTypes... args) : BaseClass(args...){}
}

तब आप किस वर्ग का उपयोग करना चाहते हैं, इस तरह घोषित किया जाएगा:

class MyObject : public PrimaryParent<SomeBaseClass>
{
public:
    MyObject() : PrimaryParent<SomeBaseClass>(SomeParams) {}
}

में आभासी विरासत उपयोग करने की आवश्यकता से बचने के लिए PrimaryParentपर BaseClass, एक निर्माता तर्क के परिवर्तनशील लेने के निर्माण की अनुमति के लिए प्रयोग किया जाता है BaseClass

पीछे कारण publicकी विरासत BaseClassमें PrimaryParentजाने के लिए है MyObjectकी विरासत से अधिक पर पूरा नियंत्रण हैBaseClass उन दोनों के बीच एक सहायक वर्ग होने के बावजूद।

इसका मतलब यह है कि आप जिस भी कक्षा में superजाना चाहते हैं, वह PrimaryParentसहायक कक्षा का उपयोग करना चाहता है , और प्रत्येक बच्चा केवल एक कक्षा से विरासत में प्राप्त कर सकता है PrimaryParent(इसलिए नाम)।

इस पद्धति के लिए एक और प्रतिबंध, MyObjectकेवल एक वर्ग को विरासत में मिल सकता है जो विरासत में मिला है PrimaryParent, और उस एक का उपयोग करके विरासत में मिला होना चाहिए PrimaryParent। यही है जो मेरा मतलब है:

class SomeOtherBase : public PrimaryParent<Ancestor>{}

class MixinClass {}

//Good
class BaseClass : public PrimaryParent<SomeOtherBase>, public MixinClass
{}


//Not Good (now 'super' is ambiguous)
class MyObject : public PrimaryParent<BaseClass>, public SomeOtherBase{}

//Also Not Good ('super' is again ambiguous)
class MyObject : public PrimaryParent<BaseClass>, public PrimaryParent<SomeOtherBase>{}

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

मल्टीपल इनहेरिटेंस एक मजबूत उपकरण है, लेकिन ज्यादातर परिस्थितियों में, केवल एक प्राथमिक माता-पिता होंगे, और यदि अन्य माता-पिता हैं, तो वे संभवत: मिक्सिन कक्षाएं, या कक्षाएं जो किसी PrimaryParentभी तरह से विरासत में नहीं आती हैं । यदि एक से अधिक वंशानुक्रम अभी भी आवश्यक है (हालांकि कई स्थितियों में विरासत के बजाय किसी वस्तु को परिभाषित करने के लिए संरचना का उपयोग करने के लिए लाभ होगा), केवल superउस वर्ग में स्पष्ट रूप से परिभाषित करने से और विरासत से नहींPrimaryParent

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

हालांकि यह सिर्फ मुझे हो सकता है।

बेशक हर स्थिति अलग होती है, लेकिन इन बातों पर विचार करें कि मैंने निर्णय लिया है कि किस विकल्प का उपयोग करना है।


1

मैं टिप्पणियों के साथ वर्तमान कोड को छोड़कर बहुत कुछ नहीं कहूंगा जो दर्शाता है कि सुपर का मतलब आधार को कॉल करना नहीं है!

super != base.

संक्षेप में, वैसे भी "सुपर" का क्या अर्थ है? और फिर "आधार" का क्या अर्थ है?

  1. सुपर का मतलब है, किसी विधि का अंतिम कार्यान्वयनकर्ता (आधार विधि नहीं)
  2. आधार का अर्थ है, यह चुनना कि कौन सा वर्ग एकाधिक उत्तराधिकार में डिफ़ॉल्ट आधार है।

यह 2 नियम कक्षा के टाइपफेड में लागू होते हैं।

पुस्तकालय कार्यान्वयनकर्ता और पुस्तकालय उपयोगकर्ता पर विचार करें, कौन सुपर है और कौन आधार है?

अधिक जानकारी के लिए यहां आपके आईडीई में कॉपी पेस्ट के लिए कोड काम कर रहा है:

#include <iostream>

// Library defiens 4 classes in typical library class hierarchy
class Abstract
{
public:
    virtual void f() = 0;
};

class LibraryBase1 :
    virtual public Abstract
{
public:
    void f() override
    {
        std::cout << "Base1" << std::endl;
    }
};

class LibraryBase2 :
    virtual public Abstract
{
public:
    void f() override
    {
        std::cout << "Base2" << std::endl;
    }
};

class LibraryDerivate :
    public LibraryBase1,
    public LibraryBase2
{
    // base is meaningfull only for this class,
    // this class decides who is my base in multiple inheritance
private:
    using base = LibraryBase1;

protected:
    // this is super! base is not super but base!
    using super = LibraryDerivate;

public:
    void f() override
    {
        std::cout << "I'm super not my Base" << std::endl;
        std::cout << "Calling my *default* base: " << std::endl;
        base::f();
    }
};

// Library user
struct UserBase :
    public LibraryDerivate
{
protected:
    // NOTE: If user overrides f() he must update who is super, in one class before base!
    using super = UserBase; // this typedef is needed only so that most derived version
    // is called, which calls next super in hierarchy.
    // it's not needed here, just saying how to chain "super" calls if needed

    // NOTE: User can't call base, base is a concept private to each class, super is not.
private:
    using base = LibraryDerivate; // example of typedefing base.

};

struct UserDerived :
    public UserBase
{
    // NOTE: to typedef who is super here we would need to specify full name
    // when calling super method, but in this sample is it's not needed.

    // Good super is called, example of good super is last implementor of f()
    // example of bad super is calling base (but which base??)
    void f() override
    {
        super::f();
    }
};

int main()
{
    UserDerived derived;
    // derived calls super implementation because that's what
    // "super" is supposed to mean! super != base
    derived.f();

    // Yes it work with polymorphism!
    Abstract* pUser = new LibraryDerivate;
    pUser->f();

    Abstract* pUserBase = new UserBase;
    pUserBase->f();
}

यहाँ एक और महत्वपूर्ण बिंदु यह है:

  1. बहुरूपी कॉल: नीचे की ओर कॉल करता है
  2. सुपर कॉल: कॉल ऊपर की ओर

अंदर main()हम बहुरूप कॉल डाउनार्ड्स का उपयोग करते हैं जो सुपर कॉल को ऊपर की ओर ले जाता है, वास्तविक जीवन में वास्तव में उपयोगी नहीं है, लेकिन यह अंतर को दर्शाता है।



0

यह एक ऐसी विधि है जिसका मैं उपयोग करता हूं जो कि टाइपसीफ के बजाय मैक्रोज़ का उपयोग करता है। मुझे पता है कि यह चीजों को करने का C ++ तरीका नहीं है, लेकिन यह सुविधाजनक हो सकता है जब विरासत के माध्यम से एक साथ चलने वालों का पीछा करते हुए जब केवल आधार वर्ग पदानुक्रम नीचे एक विरासत में मिला ऑफसेट पर कार्य कर रहा है।

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

// some header.h

#define CLASS some_iterator
#define SUPER_CLASS some_const_iterator
#define SUPER static_cast<SUPER_CLASS&>(*this)

template<typename T>
class CLASS : SUPER_CLASS {
   typedef CLASS<T> class_type;

   class_type& operator++();
};

template<typename T>
typename CLASS<T>::class_type CLASS<T>::operator++(
   int)
{
   class_type copy = *this;

   // Macro
   ++SUPER;

   // vs

   // Typedef
   // super::operator++();

   return copy;
}

#undef CLASS
#undef SUPER_CLASS
#undef SUPER

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

superजावा में देखे गए व्यवहार को दोहराने के लिए एक निम्न-केस का उपयोग किया जा सकता है लेकिन मेरी कोडिंग शैली मैक्रो के लिए सभी ऊपरी-केस अक्षरों का उपयोग करना है।

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