कंस्ट्रक्टरों को विरासत में क्यों नहीं मिले?


33

मैं उलझन में हूं कि अगर एक कंस्ट्रक्टर को बेस क्लास से विरासत में मिला है तो क्या समस्याएं हो सकती हैं। Cpp प्राइमर प्लस का कहना है,

कंस्ट्रक्टर अन्य श्रेणी के तरीकों से भिन्न होते हैं, जिसमें वे नई वस्तुओं का निर्माण करते हैं, जबकि अन्य तरीकों को मौजूदा वस्तुओं द्वारा लागू किया जाता हैयह एक कारण है कि कंस्ट्रक्टरों को विरासत में नहीं मिला है । वंशानुक्रम का अर्थ है एक व्युत्पन्न वस्तु एक बेस-क्लास विधि का उपयोग कर सकती है, लेकिन, कंस्ट्रक्टरों के मामले में, ऑब्जेक्ट तब तक मौजूद नहीं होता है जब तक कि कंस्ट्रक्टर ने अपना काम नहीं किया है।

मैं समझता हूं कि ऑब्जेक्ट का निर्माण पूरा होने से पहले कंस्ट्रक्टर को बुलाया जाता है।

यदि बच्चा वर्ग विरासत में मिला है (तो इसका मतलब है कि बच्चे की कक्षा अभिभावक वर्ग विधि आदि को ओवरराइड करने में सक्षम है, अभिभावक वर्ग विधि तक नहीं पहुंच पा रहे हैं )

मैं समझता हूं कि एक कोड के भीतर एक स्पष्ट रूप से एक रचनाकार को कॉल करने की कोई आवश्यकता नहीं है [ऐसा नहीं है कि मुझे अभी तक पता है] वस्तुओं को बनाते समय छोड़कर। फिर भी आप कुछ तंत्र का उपयोग करके माता-पिता की कमी को रोकने के लिए उपयोग कर सकते हैं [सीपीपी में, उपयोग ::या उपयोग करते हुए member initialiser list, जावा का उपयोग करते हुए super]। जावा में इसे 1 पंक्ति में कॉल करने के लिए एक प्रवर्तन है, मैं समझता हूं कि यह सुनिश्चित करना है कि माता-पिता की वस्तु पहले बनाई गई है और फिर बाल वस्तु निर्माण आय।

यह इसे ओवरराइड कर सकता है। लेकिन, मैं उन स्थितियों के साथ नहीं आ सकता, जहां यह एक समस्या पैदा कर सकता है। यदि बच्चा मूल निर्माणकर्ता को विरासत में देता है तो क्या गलत हो सकता है?

तो यह सिर्फ आवश्यक कार्यों को विरासत में प्राप्त करने से दूर रखने के लिए है। या इसका कोई आगे का हिस्सा है?


वास्तव में C ++ 11 के साथ गति करने के लिए नहीं, लेकिन मुझे लगता है कि इसमें कंस्ट्रक्टर इनहेरिटेंस का कुछ रूप है।
यनीस

3
ध्यान रखें कि आप कंस्ट्रक्टरों में जो कुछ भी करते हैं, आपको विनाशकों का ध्यान रखना होगा।
मनोज आर

2
मुझे लगता है कि आपको परिभाषित करने की आवश्यकता है कि "एक निर्माणकर्ता को विरासत में मिला" क्या है। सभी जवाबों से लगता है कि वे "एक निर्माणकर्ता को विरासत में मिला" का मतलब क्या है? मुझे नहीं लगता कि उनमें से कोई भी मेरी प्रारंभिक व्याख्या से मेल खाता है। वर्णन "इनहेरिटेंस" ऊपर से "ग्रे बॉक्स में" का अर्थ है कि एक व्युत्पन्न वस्तु एक बेस-क्लास विधि का उपयोग कर सकती है जिसका अर्थ है कि निर्माणकर्ता विरासत में मिले हैं। एक व्युत्पन्न वस्तु सबसे निश्चित रूप से बेस-क्लास कंस्ट्रक्टर विधियों का उपयोग कर सकती है, हमेशा।
डंक

1
एक कंस्ट्रक्टर को विरासत में प्राप्त करने के स्पष्टीकरण के लिए धन्यवाद, अब आप कुछ उत्तर प्राप्त कर सकते हैं।
डंक

जवाबों:


41

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

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

C ++ 11 में, 'कंस्ट्रक्टर इनहेरिटेंस' का एक रूप पेश किया गया है, जहाँ आप कंपाइलर को निर्देश दे सकते हैं कि वह आपके लिए कंस्ट्रक्टर का एक सेट जनरेट करे जो बेस क्लास से कंस्ट्रक्टर्स के समान तर्क ले और बस उन तर्कों को आगे बढ़ाएं आधार वर्ग।
हालाँकि इसे आधिकारिक तौर पर वंशानुक्रम कहा जाता है, यह वास्तव में ऐसा नहीं है क्योंकि अभी भी एक व्युत्पन्न वर्ग विशिष्ट कार्य है। अब यह केवल संकलक द्वारा आपके द्वारा स्पष्ट रूप से लिखे जाने के बजाय उत्पन्न होता है।

यह फीचर इस तरह काम करता है:

struct Base {
    Base(int a) : i(a) {}
    int i;
};

struct Derived : Base {
    Derived(int a, std::string s) : Base(a), m(s) {}

    using Base::Base; // Inherit Base's constructors.
    // Equivalent to:
    //Derived(int a) : Base(a), m() {}

    std::string m;
};

Derivedअब दो कंस्ट्रक्टर हैं (कॉपी / मूवर्स की गिनती नहीं कर रहे हैं)। एक जो एक इंट और एक स्ट्रिंग लेता है और एक जो सिर्फ एक इंट लेता है।


तो यह मान लिया गया है, कि बाल वर्ग का अपना निर्माता नहीं होगा? और विरासत में मिला निर्माण स्पष्ट रूप से बच्चे के सदस्यों और अनजान होने के लिए अनभिज्ञ है
सुवर्ण पट्टायल

सी ++ इस तरह से परिभाषित किया गया है कि यह असंभव है एक वर्ग के लिए नहीं अपने स्वयं के निर्माता (रों) है। यही कारण है कि मैं पर विचार नहीं करते 'निर्माता विरासत' वास्तव में करने के लिए है हो सकता है विरासत। यह थकाऊ बॉयलरप्लेट लिखने से बचने का एक तरीका है।
बार्ट वैन इनगेन शेनौ

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

1
कृपया यह इंगित करने पर विचार करें कि कहां से m()आता है, और यदि इसका प्रकार उदाहरण के लिए है तो यह कैसे बदलेगा int
डिडुप्लिकेटर

क्या यह using Base::Baseसम्‍मिलित रूप से करना संभव है ? इसके बड़े परिणाम होंगे यदि मैं उस रेखा को एक व्युत्पन्न वर्ग पर भूल गया और मुझे सभी व्युत्पन्न वर्गों में कंस्ट्रक्टर को इनहेरिट करने की आवश्यकता है
पोस्ट सेल्फ

7

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

"वर्चुअल कंस्ट्रक्टर" पैटर्न के बाहर पॉलीमॉर्फिक कंस्ट्रक्टर्स के लिए बहुत कम उपयोगिता है, और एक ठोस परिदृश्य के साथ आना मुश्किल है जहां एक वास्तविक पॉलीमॉर्फिक कंस्ट्रक्टर का उपयोग किया जा सकता है। एक बहुत ही वंचित उदाहरण जो किसी भी तरह से दूर से वैध C ++ नहीं है :

struct Base {
  virtual Base(unsigned p1, unsigned p2) {...}
};

struct Derived: public Base {
  Derived(unsigned p1, unsigned p2) : Base(p1, p2) override {...}
};

int main(void) {
  unsigned p1 = 0;
  unsigned p2 = 42;
  Derived *p_d1 = new Base(p1, p2); // This might call Derived(unsigned, unsigned).
  Derived *p_d2 = nullptr;
  p_d2 = new Base(p1, p2); // This might call Derived(unsigned, unsigned) too.
}

इस मामले में कहा जाता है कि कंस्ट्रक्टर कंक्रीट के प्रकार पर निर्भर करता है जिसका निर्माण या सौंपा जा रहा है। पार्सिंग / कोड जनरेशन के दौरान इसका पता लगाना जटिल है और इसकी कोई उपयोगिता नहीं है: आप जानते हैं कि आप जिस ठोस प्रकार का निर्माण कर रहे हैं और आपने व्युत्पन्न वर्ग के लिए एक विशिष्ट कंस्ट्रक्टर लिखा है। निम्न मान्य C ++ कोड बिल्कुल वैसा ही है, थोड़ा कम है और यह जो करता है उसमें अधिक स्पष्ट है:

struct Base {
  Base(unsigned p1, unsigned p2) {...}
};

struct Derived: public Base {
  Derived(unsigned p1, unsigned p2) : Base(p1, p2) {...}
};

int main(void) {
  unsigned p1 = 0;
  unsigned p2 = 42;
  Derived *p_d1 = new Derived(p1, p2); 
  Derived *p_d2 = nullptr;
  p_d2 = new Derived(p1, p2);
}


एक दूसरी व्याख्या या शायद अतिरिक्त प्रश्न है - क्या होगा यदि बेस क्लास कंस्ट्रक्टर स्वचालित रूप से सभी व्युत्पन्न वर्गों में मौजूद थे जब तक कि स्पष्ट रूप से छिपाया न जाए।

यदि बच्चा मूल निर्माणकर्ता को विरासत में देता है तो क्या गलत हो सकता है?

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

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

struct Rectangle {
  Rectangle(unsigned width, unsigned height) {...}
};

struct Square : public Rectangle {
  explicit Square(unsigned side) : Rectangle(side, side) {...}
};

यदि स्क्वायर को आयत के दो-मूल्य वाले निर्माता विरासत में मिले, तो आप एक अलग ऊंचाई और चौड़ाई के साथ वर्गों का निर्माण कर सकते हैं ... यह तार्किक रूप से गलत है, इसलिए आप उस निर्माता को छिपाना चाहेंगे।


3

निर्माणकर्ताओं को विरासत में क्यों नहीं मिला: इसका उत्तर आश्चर्यजनक रूप से सरल है: बेस क्लास का निर्माता बेस क्लास को बनाता है और विरासत में मिला वर्ग का निर्माण "विरासत में मिला वर्ग" बनाता है। यदि उत्तराधिकारी वर्ग निर्माणकर्ता को विरासत में देगा, तो निर्माणकर्ता प्रकार बेस क्लास का एक ऑब्जेक्ट बनाने की कोशिश करेगा और आप विरासत में मिले वर्ग के ऑब्जेक्ट का "निर्माण" करने में सक्षम नहीं होंगे।

किसी वर्ग को विरासत में देने का उद्देश्य किस तरह का है।


3

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

इसके अलावा, लिस्कोव-प्रतिस्थापन सिद्धांत अब लागू नहीं होगा क्योंकि अब आप आधार श्रेणी की वस्तुओं के अपने संग्रह पर एक दूसरे के साथ संगत होने के लिए भरोसा नहीं कर सकते हैं, क्योंकि इस बात की कोई गारंटी नहीं है कि आधार वर्ग का निर्माण अन्य व्युत्पन्न प्रकारों के साथ ठीक से या अनुकूलता से किया गया था।

जब विरासत का 1 से अधिक स्तर जोड़ा जाता है तो यह और जटिल हो जाता है। अब आपके व्युत्पन्न वर्ग को यह जानने की जरूरत है कि श्रृंखला के सभी आधार वर्गों का निर्माण कैसे किया जाए।

तब क्या होता है यदि आप वंशानुक्रम पदानुक्रम के शीर्ष पर एक नया आधार वर्ग जोड़ते हैं? आपको सभी व्युत्पन्न वर्ग निर्माणकर्ताओं को अपडेट करना होगा।


2

कंस्ट्रक्टर अन्य तरीकों से मौलिक रूप से अलग हैं:

  1. यदि आप उन्हें नहीं लिखते हैं तो वे उत्पन्न होते हैं।
  2. सभी बेस क्लास कंस्ट्रक्टर्स को वैसे भी कहा जाता है, भले ही आप इसे मैन्युअल रूप से न करें
  3. आप उन्हें स्पष्ट रूप से नहीं बल्कि वस्तुओं का निर्माण करके कहते हैं।

तो वे विरासत में क्यों नहीं हैं? सरल उत्तर: क्योंकि हमेशा एक ओवरराइड होता है, या तो मैन्युअल रूप से उत्पन्न होता है या लिखा जाता है।

हर वर्ग को एक निर्माता की आवश्यकता क्यों है? यह एक जटिल प्रश्न है और मेरा मानना ​​है कि उत्तर संकलक पर निर्भर है। "तुच्छ" निर्माता के रूप में ऐसी कोई चीज है जिसके लिए संकलक अनिवार्य नहीं करता है कि इसे कहा जाता है ऐसा प्रतीत होता है। मुझे लगता है कि विरासत से आपके मतलब की सबसे करीबी चीज है, लेकिन ऊपर बताए गए तीन कारणों से मुझे लगता है कि निर्माणकर्ताओं की सामान्य तरीकों से तुलना करना वास्तव में उपयोगी नहीं है। :)


1

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

Class A : public B {
using B:B;
....
  1. इनहेरिटिंग कंस्ट्रक्टर्स का एक सेट से बना है

    • बेस क्लास के सभी गैर-टेम्प्लेट कंस्ट्रक्टर (एलिप्सिस मापदंडों को छोड़ने के बाद, यदि कोई हो) (C ++ 14 के बाद से)
    • डिफ़ॉल्ट तर्क या दीर्घवृत्त पैरामीटर के साथ प्रत्येक निर्माणकर्ता के लिए, सभी निर्माण हस्ताक्षर जो कि दीर्घवृत्त छोड़ने और तर्क के सिरों से डिफ़ॉल्ट तर्क छोड़ने से बनते हैं, एक-एक करके सूचीबद्ध करता है
    • बेस क्लास के सभी कंस्ट्रक्टर टेम्प्लेट (एलिप्सिस मापदंडों को छोड़ने के बाद, यदि कोई हो) (C ++ 14 के बाद से)
    • डिफ़ॉल्ट तर्क या दीर्घवृत्त के साथ प्रत्येक रचनाकार टेम्पलेट के लिए, तर्क के सिरों से दीर्घवृत्त और डिफ़ॉल्ट तर्क को छोड़ने के द्वारा बनने वाले सभी निर्माण हस्ताक्षर एक-एक करके सूचीबद्ध होते हैं
  2. सभी अंतर्निहित निर्माणकर्ता जो डिफ़ॉल्ट कंस्ट्रक्टर या कॉपी / मूव कंस्ट्रक्टर नहीं हैं और जिनके हस्ताक्षर व्युत्पन्न वर्ग में उपयोगकर्ता-परिभाषित कंस्ट्रक्टर्स से मेल नहीं खाते हैं, उन्हें व्युत्पन्न वर्ग में स्पष्ट रूप से घोषित किया गया है। डिफ़ॉल्ट पैरामीटर विरासत में नहीं मिले हैं


0

आप उपयोग कर सकते हैं:

MyClass() : Base()

क्या आप पूछ रहे हैं क्यों आपको ऐसा करना है?

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

आप उप-प्रकार ऑब्जेक्ट कैसे बनाएंगे?


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