2008-08-31 10 views
12

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

class base { 
public: 
    base() 
    { 
     std::cout << "foo is " << foo() << std::endl; 
    } 
    virtual int foo() { return 42; } 
}; 

class derived : public base { 
    int* ptr_; 
public: 
    derived(int i) : ptr_(new int(i*i)) { } 
    // The following cannot be called before derived::derived due to how C++ behaves, 
    // if it was possible... Kaboom! 
    virtual int foo() { return *ptr_; } 
}; 

यह बिल्कुल जावा के लिए ही है और .NET फिर भी वे दूसरे रास्ते से जाने के लिए चुना है, एकमात्र कारण कम से कम के सिद्धांत आश्चर्य था?

आपको क्या लगता है कि सही विकल्प है?

उत्तर

11

कैसे भाषाओं एक वस्तु के जीवन समय को परिभाषित करने में एक मौलिक अंतर नहीं है। जावा में और .NET ऑब्जेक्ट सदस्यों को किसी भी कन्स्ट्रक्टर चलाने से पहले शून्य/शून्य प्रारंभ किया जाता है और इस बिंदु पर ऑब्जेक्ट जीवनकाल शुरू होता है। तो जब आप कन्स्ट्रक्टर में प्रवेश करते हैं तो आपको पहले से ही प्रारंभिक ऑब्जेक्ट मिल गया है।

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

ऑब्जेक्ट जीवनकाल की जावा/.Net परिभाषा के साथ समस्या यह है कि यह सुनिश्चित करना कठिन होता है कि ऑब्जेक्ट प्रारंभ होने पर ऑब्जेक्ट प्रारंभ होने पर ऑब्जेक्ट हमेशा अपने इनवेरिएंट को पूरा करता है लेकिन कन्स्ट्रक्टर नहीं चलता है।सी ++ परिभाषा के साथ समस्या यह है कि आपके पास यह विषम अवधि है जहां वस्तु कमजोर है और पूरी तरह से निर्मित नहीं है।

7

दोनों तरीकों से अप्रत्याशित परिणाम हो सकते हैं। आपकी सबसे अच्छी शर्त है कि आप अपने कन्स्ट्रक्टर में वर्चुअल फ़ंक्शन को कॉल न करें।

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

1

मुझे लगता है कि सी ++ 'सबसे सही' व्यवहार होने के मामले में सबसे अच्छा अर्थशास्त्र प्रदान करता है ... हालांकि यह कंपाइलर के लिए अधिक काम है और कोड निश्चित रूप से इसे पढ़ने वाले किसी के लिए अंतर्ज्ञानी नहीं है।

.NET दृष्टिकोण के साथ फ़ंक्शन बहुत सीमित होना चाहिए कि किसी व्युत्पन्न ऑब्जेक्ट स्थिति पर भरोसा न करें।

+1

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

+0

@LucTouraille बहुत बेल्ट +1, यह बहुत सच है। सी ++ दृष्टिकोण वास्तव में बहुत साफ और अधिक सरल है –

2

रचनाकारों में वर्चुअल फ़ंक्शंस, भाषाएं अलग क्यों होती हैं?

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

लेकिन कभी-कभी, जहां मैं राज्य को प्रारंभ करने के लिए वर्चुअल फ़ंक्शंस का उपयोग करना चाहता हूं (इसलिए इससे कोई फर्क नहीं पड़ता कि उन्हें राज्य को अनियंत्रित किया जा रहा है) सी #/जावा व्यवहार अच्छा है।

0

डेल्फी VCL जीयूआई ढांचे में आभासी निर्माताओं की अच्छा उपयोग करता है:

type 
    TComponent = class 
    public 
    constructor Create(AOwner: TComponent); virtual; // virtual constructor 
    end; 

    TMyEdit = class(TComponent) 
    public 
    constructor Create(AOwner: TComponent); override; // override virtual constructor 
    end; 

    TMyButton = class(TComponent) 
    public 
    constructor Create(AOwner: TComponent); override; // override virtual constructor 
    end; 

    TComponentClass = class of TComponent; 

function CreateAComponent(ComponentClass: TComponentClass; AOwner: TComponent): TComponent; 
begin 
    Result := ComponentClass.Create(AOwner); 
end; 

var 
    MyEdit: TMyEdit; 
    MyButton: TMyButton; 
begin 
    MyEdit := CreateAComponent(TMyEdit, Form) as TMyEdit; 
    MyButton := CreateAComponent(TMyButton, Form) as TMyButton; 
end; 
0

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

BaseClass() { for (int i=0; i<virtualSize(); i++) initialize_stuff_for_index(i); }

फिर सी की फिर से लाभ ++ व्यवहार है कि यह लिखा जा रहा से ऊपर की तरह constuctors हतोत्साहित है।

मुझे नहीं लगता कि कन्स्ट्रक्टर को समाप्त करने वाले तरीकों को कॉल करने की समस्या सी ++ के लिए एक अच्छा बहाना है। यदि यह वास्तव में एक समस्या थी तो कन्स्ट्रक्टर को किसी भी विधियों को कॉल करने की अनुमति नहीं दी जाएगी, क्योंकि एक ही समस्या बेस क्लास के तरीकों पर लागू हो सकती है।

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

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

यह और तथ्य यह है कि सी ++ बाहर निकलने पर स्थैतिक वस्तुओं पर विनाशकों को कॉल करता है, वास्तव में मुझे लंबे समय तक बंद कर देता है।

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