2008-09-23 14 views
517

सुपरक्लास निर्माता से सुपरक्लास कन्स्ट्रक्टर को कॉल करने के लिए सी ++ नियम क्या हैं?सुपरक्लास कन्स्ट्रक्टर को कॉल करने के नियम क्या हैं?

उदाहरण के लिए, मुझे जावा में पता है, आपको इसे सबक्लास कन्स्ट्रक्टर की पहली पंक्ति के रूप में करना होगा (और यदि आप नहीं करते हैं, तो नो-एर्ग सुपर कन्स्ट्रक्टर को एक निहित कॉल माना जाता है - आपको एक संकलन त्रुटि दे रहा है अगर वह गुम है)।

उत्तर

717

बेस क्लास कन्स्ट्रक्टर स्वचालित रूप से आपके लिए बुलाए जाते हैं यदि उनके पास कोई तर्क नहीं है। यदि आप किसी तर्क के साथ सुपरक्लस कन्स्ट्रक्टर को कॉल करना चाहते हैं, तो आपको उप-वर्ग की कन्स्ट्रक्टर प्रारंभिक सूची का उपयोग करना होगा। जावा के विपरीत, सी ++ एकाधिक विरासत (बेहतर या बदतर के लिए) का समर्थन करता है, इसलिए बेस क्लास को "सुपर()" के बजाय नाम से संदर्भित किया जाना चाहिए। निर्माता के प्रारंभ सूची here और here पर

class SuperClass 
{ 
    public: 

     SuperClass(int foo) 
     { 
      // do something with foo 
     } 
}; 

class SubClass : public SuperClass 
{ 
    public: 

     SubClass(int foo, int bar) 
     : SuperClass(foo) // Call the superclass constructor in the subclass' initialization list. 
     { 
      // do something with bar 
     } 
}; 

अधिक जानकारी।

+36

मैं सुपर क्लास निर्माता से 'स्पष्ट' हटा दिया। सिंगल-तर्क कन्स्ट्रक्टर के लिए सबसे अच्छा अभ्यास होने के बावजूद, यह चर्चा के लिए जर्मन नहीं था। स्पष्ट कुंजी शब्द के बारे में अधिक जानकारी के लिए, देखें: http://weblogs.asp.net/kennykerr/archive/2004/08/31/Explicit-Constructors.aspx – luke

+1

कोलन: ऑपरेटर आप बच्चे को तत्काल करने से पहले सुपरक्लास कन्स्ट्रक्टर का आह्वान करते थे वर्ग निर्माता, मुझे लगता है कि यह विधियों के लिए भी सच है? – ha9u63ar

+2

@hagubear, केवल रचनाकारों के लिए मान्य है, AFAIK – luke

14

यदि आपके पास तर्क के बिना कोई कन्स्ट्रक्टर है तो इसे व्युत्पन्न क्लास कन्स्ट्रक्टर निष्पादित करने से पहले बुलाया जाएगा।

आप तर्क आप स्पष्ट रूप से लिखने के लिए है कि इस तरह ली गई निर्माता में है के साथ एक आधार निर्माता कॉल करने के लिए करना चाहते हैं:

class base 
{ 
    public: 
    base (int arg) 
    { 
    } 
}; 

class derived : public base 
{ 
    public: 
    derived() : base (number) 
    { 
    } 
}; 

आप सी में माता-पिता निर्माता बुला ++ के बिना एक व्युत्पन्न वर्ग का निर्माण नहीं कर सकते। यह स्वचालित रूप से होता है यदि यह एक गैर-तर्क C'tor है, तो ऐसा होता है यदि आप ऊपर दिखाए गए व्युत्पन्न कन्स्ट्रक्टर को सीधे कॉल करते हैं या आपका कोड संकलित नहीं होगा।

16

अभिभावक कन्स्ट्रक्टर को मान पास करने का एकमात्र तरीका प्रारंभिक सूची के माध्यम से है। Initilization सूची एक के साथ कार्यान्वित किया गया है: और उसके बाद कक्षाओं और मूल्यों की एक सूची उस वर्ग निर्माता को पारित किया जाना है।

Class2::Class2(string id) : Class1(id) { 
.... 
} 

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

174

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

class Sub : public Base 
{ 
    Sub(int x, int y) 
    : Base(x), member(y) 
    { 
    } 
    Type member; 
}; 

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

class Sub : public Base 
{ 
    Sub(int x, int y) 
    try : Base(x), member(y) 
    { 
    // function body goes here 
    } catch(const ExceptionType &e) { 
    throw kaboom(); 
    } 
    Type member; 
}; 

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

+1

मुझे यकीन नहीं है कि मैं आपके दूसरे उदाहरण के वाक्यविन्यास को समझता हूं ... क्या कोशिश/पकड़ निर्माता के शरीर के लिए एक प्रतिस्थापन का निर्माण है? – levik

+2

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

+76

प्रारंभकर्ताओं के लिए प्रयास/पकड़ वाक्यविन्यास शामिल करने के लिए धन्यवाद। मैं 10 साल के लिए सी ++ का उपयोग कर रहा हूं, और यह पहली बार मैंने कभी देखा है। – jakar

6
CDerived::CDerived() 
: CBase(...), iCount(0) //this is the initialisation list. You can initialise member variables here too. (e.g. iCount := 0) 
    { 
    //construct body 
    } 
40

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


class A : public B 
{ 
public: 
    A(int a, int b, int c); 
private: 
    int b_, c_; 
}; 

फिर, बी संभालने एक निर्माता है जो एक पूर्णांक लेता है, एक के निर्माता इस तरह दिखना हो सकता है:


A::A(int a, int b, int c) 
    : B(a), b_(b), c_(c) // initialization list 
{ 
    // do something 
} 

आप देख सकते हैं, के निर्माता बेस क्लास प्रारंभिक सूची में बुलाया जाता है। प्रारंभिक सूची में डेटा सदस्यों को प्रारंभ करना, वैसे, कन्स्ट्रक्टर के शरीर के अंदर b_, और c_ के मानों को असाइन करना बेहतर है, क्योंकि आप असाइनमेंट की अतिरिक्त लागत को सहेज रहे हैं।

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

+1

एक सेकंड प्रतीक्षा करें ... आप कहते हैं कि प्रारंभिक कार्यकर्ताओं की लागत पर बचत होती है। लेकिन अगर बुलाया जाता है तो उनके अंदर एक ही असाइनमेंट नहीं होता है? – levik

+5

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

+1

और यदि आप शरीर के अंदर असाइनमेंट का उपयोग करते हैं, तो आप वैसे भी प्रारंभिक लागत लेते हैं, और उसके बाद असाइनमेंट की लागत। – Dima

11

सभी ने प्रारंभिक सूची के माध्यम से एक निर्माता कॉल का उल्लेख किया, लेकिन किसी ने भी नहीं कहा कि अभिभावक वर्ग के निर्माता को व्युत्पन्न सदस्य के निर्माता के शरीर से स्पष्ट रूप से बुलाया जा सकता है। उदाहरण के लिए, Calling a constructor of the base class from a subclass' constructor body प्रश्न देखें। बिंदु यह है कि यदि आप व्युत्पन्न वर्ग के शरीर में अभिभावक वर्ग या सुपर क्लास कन्स्ट्रक्टर को एक स्पष्ट कॉल का उपयोग करते हैं, तो यह वास्तव में केवल मूल वर्ग का एक उदाहरण बना रहा है और यह व्युत्पन्न पर अभिभावक वर्ग निर्माता का आविष्कार नहीं कर रहा है वस्तु। एक व्युत्पन्न वर्ग 'ऑब्जेक्ट पर अभिभावक वर्ग या सुपर क्लास कन्स्ट्रक्टर का आह्वान करने का एकमात्र तरीका प्रारंभिक सूची के माध्यम से है और व्युत्पन्न वर्ग कन्स्ट्रक्टर बॉडी में नहीं है। तो शायद इसे "सुपरक्लस कन्स्ट्रक्टर कॉल" नहीं कहा जाना चाहिए। मैंने यह जवाब यहां दिया क्योंकि कोई भ्रमित हो सकता है (जैसा मैंने किया)।

+10

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

+0

@ रिचर्ड चेम्बर यह शायद भ्रमित हो रहा है क्योंकि अंग्रेजी मेरी पहली भाषा नहीं है, लेकिन आपने ठीक से वर्णन किया है कि मैंने क्या कहने की कोशिश की। –

1

किसी वर्ग ने कई कक्षाओं से प्राप्त होने पर कन्स्ट्रक्टर कॉल के अनुक्रम का उल्लेख नहीं किया है। वर्गों को प्राप्त करते समय अनुक्रम का उल्लेख किया गया है।

+2

यदि कोई इसके बारे में बात नहीं करता है, तो इसका उल्लेख क्यों किया गया था? – EJP

+3

@EJP चूंकि प्रश्न नियमों को कॉल करने के बारे में है, तो जवाब –

7

यदि आपके बेस कंस्ट्रक्टर में डिफ़ॉल्ट पैरामीटर हैं तो बेस क्लास स्वचालित रूप से कॉल हो जाएगी।

using namespace std; 

class Base 
{ 
    public: 
    Base(int a=1) : _a(a) {} 

    protected: 
    int _a; 
}; 

class Derived : public Base 
{ 
    public: 
    Derived() {} 

    void printit() { cout << _a << endl; } 
}; 

int main() 
{ 
    Derived d; 
    d.printit(); 
    return 0; 
} 

आउटपुट: 1

+0

में कॉल करने के अनुक्रम का उल्लेख करना उचित है क्योंकि यह विशेष घोषणा एक अंतर्निहित 'आधार()' बनाती है, जिसका आधार 'आधार (int) 'लेकिन _: _a {1}' के लिए एक निहित प्रारंभकर्ता। यह 'बेस()' है जिसे हमेशा कॉल किया जाता है यदि इन-लिस्ट में कोई विशिष्ट आधार कन्स्ट्रक्टर जंजीर नहीं है। और, जैसा कि कहीं और बताया गया है, सी ++ 11 के प्रतिनिधि रचनाकार और ब्रेस-या-बराबर प्रारंभिकरण डिफ़ॉल्ट तर्कों को कम आवश्यक बनाते हैं (जब वे पहले से ही कई उदाहरणों में कोड-गंध-एस्क्यू थे)। –

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