2011-01-29 10 views
30

मैं उदाहरण के लिए एक constructor जब नहीं घोषित करते हैं, संकलक एक default constructor कोई तर्क और कोई परिभाषा (शरीर) होगा साथ मुझे प्रदान करेगा, और इस प्रकार, कोई कार्रवाई नहीं की ले जाएगा।सी ++ डिफ़ॉल्ट नाशक

अब मैं एक destructor घोषित नहीं करते, संकलक कोई defintion (शरीर) के साथ एक default destructor के साथ मुझे प्रदान करेगा, और इस तरह, मैं कोई कार्रवाई नहीं की लगता है।

तो, यदि मैं उदाहरण के लिए किसी ऑब्जेक्ट के साथ समाप्त कर चुका हूं, तो default destructor ऑब्जेक्ट द्वारा उपयोग की जाने वाली रीयलोकेट (फ्री) मेमोरी नहीं होगी? अगर ऐसा नहीं होता है, तो हम इसे क्यों प्राप्त कर रहे हैं?

और, शायद वही प्रश्न default constructor पर लागू होता है। यदि यह कुछ भी नहीं करता है, तो यह डिफ़ॉल्ट रूप से हमारे लिए क्यों बनाया गया है?

धन्यवाद।

+19

निर्माता वस्तु की स्मृति को आबंटित नहीं करता है, स्मृति पहले से ही किसी भी तरह इसके लिए आवंटित किया जाता है कन्स्ट्रक्टर कहने से पहले। – dreamlax

+0

इस प्रश्न को प्रस्तुत करते समय बहुत से गलत वक्तव्य। और उन पर इशारा करते हुए जवाब स्वीकार नहीं किया जाता है। – Antonio

उत्तर

0

एक डिफ़ॉल्ट विनाशक को यह जानने का कोई तरीका नहीं होगा कि आपकी कक्षा "स्वामित्व" को मुक्त करने में सक्षम होने के लिए कौन सी मेमोरी है।

डिफ़ॉल्ट निर्माता भाग के रूप में, मैं इस पर Wikipedia article बोली होगा ...

सी ++ में, डिफ़ॉल्ट कंस्ट्रक्टर्स महत्वपूर्ण है क्योंकि वे स्वचालित रूप से कुछ परिस्थितियों में लागू कर रहे हैं:

  • जब किसी ऑब्जेक्ट मान को कोई तर्क सूची घोषित नहीं किया जाता है, उदाहरण के लिए MyClass x ;; या तर्क सूची के साथ गतिशील रूप से आवंटित, उदा। नया MyClass; डिफ़ॉल्ट कन्स्ट्रक्टर पर प्रयुक्त होता है ऑब्जेक्ट
  • प्रारंभ करें जब वस्तुओं की एक सरणी घोषित की जाती है, उदा। MyClass एक्स [10] ;; या गतिशील रूप से आवंटित, उदा। नया माइक्लास [10]; डिफ़ॉल्ट निर्माता सभी तत्वों
  • जब एक व्युत्पन्न वर्ग निर्माता स्पष्ट रूप से अपने प्रारंभकर्ता सूची में आधार वर्ग निर्माता फोन नहीं करता है, आधार वर्ग के लिए डिफ़ॉल्ट निर्माता
  • जब एक वर्ग कहा जाता है प्रारंभ करने में प्रयोग किया जाता है निर्माता स्पष्ट रूप से अपनी प्रारंभकर्ता सूची में अपनी वस्तु-मान क्षेत्रों में एक के निर्माता फोन नहीं करता है, क्षेत्र के वर्ग के लिए डिफ़ॉल्ट निर्माता
  • बुलाया मानक पुस्तकालय में है, कुछ कंटेनरों "भरण" मानों का उपयोग डिफ़ॉल्ट कन्स्ट्रक्टर जब मान स्पष्ट रूप से नहीं दिया गया है, उदा। वेक्टर (10); वेक्टर को 10 तत्वों के साथ प्रारंभ करता है, जो हैं जो हमारे प्रकार के डिफ़ॉल्ट-निर्मित मान से भरे हुए हैं।

ऊपर परिस्थितियों में, यह एक त्रुटि है, तो वर्ग एक डिफ़ॉल्ट निर्माता नहीं है।कंपाइलर स्पष्ट रूप से डिफ़ॉल्ट कन्स्ट्रक्टर

परिभाषित करता है यदि कोई निर्माता स्पष्ट रूप से किसी वर्ग के लिए परिभाषित नहीं है। यह निहित रूप से घोषित डिफ़ॉल्ट कन्स्ट्रक्टर एक खाली शरीर के साथ परिभाषित एक डिफ़ॉल्ट कन्स्ट्रक्टर के बराबर है। (नोट:। अगर कुछ कंस्ट्रक्टर्स हैं परिभाषित, लेकिन वे सभी गैर-डिफ़ॉल्ट हैं, संकलक एक डिफ़ॉल्ट निर्माता को परिभाषित परोक्ष नहीं होगा यह मतलब यह है कि एक डिफ़ॉल्ट निर्माता एक वर्ग के लिए मौजूद नहीं हो सकता है।)

4

क्योंकि यदि आपके पास कोई (सार्वजनिक रूप से सुलभ) रचनाकार या विनाशक नहीं है, तो कक्षा का एक वस्तु तत्काल नहीं किया जा सकता है। पर विचार करें:

class A 
{ 
private: 
    A() {} 
    ~A() {} 
}; 

A a; // Oh dear! Compilation error 

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

+1

संकलन त्रुटि क्यों? क्या पहले से कोई डिफॉल्ट कन्स्ट्रक्टर नहीं है? धन्यवाद – Simplicity

+2

@user: नहीं, क्योंकि हमने डिफ़ॉल्ट कन्स्ट्रक्टर को निजी के रूप में घोषित कर दिया है। इसलिए, इसे लागू नहीं किया जा सकता है (एक स्थिर वर्ग सदस्य समारोह के माध्यम से)। –

1

संक्षिप्त उत्तर यह है कि सी ++ में प्रत्येक ऑब्जेक्ट को एक कन्स्ट्रक्टर और विनाशक की आवश्यकता होती है, भले ही वे कुछ भी न करें। तो पृष्ठभूमि में आपके लिए उन्हें बनाते हुए संकलक इस आवश्यकता को पूरा करता है।

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

4

डिफ़ॉल्ट विनाशक कुछ भी नहीं करेगा (बस एक डिफ़ॉल्ट कन्स्ट्रक्टर की तरह)।

यदि आपके विनाशक को वास्तव में कुछ करने की आवश्यकता है (उदाहरण: कुछ संसाधनों को मुक्त करना) तो आपको खुद को परिभाषित करने की आवश्यकता होगी।

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

डिफ़ॉल्ट कन्स्ट्रक्टर/विनाशक/असाइनमेंट ऑपरेटर/कॉपी कन्स्ट्रक्टर उपयोगी होते हैं जब आप साधारण कक्षाएं प्रबंधित कर रहे हैं जहां आपको कुछ भी करने की आवश्यकता नहीं है। एक विशेष मामला पीओडी है: वे (सी ++ 0x से पहले) में स्पष्ट रचनाकार या विनाशक भी नहीं हो सकते हैं।

3

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

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

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

49

यह कहना गलत है कि एक कंपाइलर से उत्पन्न डिफ़ॉल्ट कन्स्ट्रक्टर कोई कार्रवाई नहीं करता है। यह एक खाली शरीर और एक खाली प्रारंभकर्ता सूची के साथ उपयोगकर्ता द्वारा परिभाषित कन्स्ट्रक्टर के बराबर है, लेकिन इसका मतलब यह नहीं है कि इसमें कोई कार्रवाई नहीं होती है। यहां यह है कि यह करता है:

  1. यह बेस क्लास के डिफ़ॉल्ट कन्स्ट्रक्टर को कॉल करता है।
  2. क्लास पॉलीमोर्फिक है, तो यह vtable सूचक को प्रारंभ करता है।
  3. यह उन सभी सदस्यों के डिफ़ॉल्ट निर्माता कहते हैं जिनके पास है। यदि कुछ रचनाकारों के साथ कोई सदस्य है, लेकिन डिफ़ॉल्ट के बिना, तो यह संकलन-समय त्रुटि है।

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

वही विनाशक के लिए जाता है - यह बेस क्लास के विनाशक और सभी सदस्यों के विनाशकों को बुलाता है, इसलिए यह सामान्य स्थिति में सच नहीं है कि एक कंपाइलर से उत्पन्न विनाशक कुछ भी नहीं करता है।

लेकिन स्मृति आवंटन वास्तव में इसके साथ कुछ लेना देना नहीं है। रचनाकार कहने से पहले स्मृति आवंटित की जाती है, और अंतिम विनाशक समाप्त होने के बाद ही इसे मुक्त किया जाता है।

+1

निष्पक्ष होने के लिए, यह सब "दृश्यों के पीछे" होता है। यह उपयोगकर्ता द्वारा घोषित कन्स्ट्रक्टर के साथ भी होता है, भले ही आप अपने कन्स्ट्रक्टर में कोई कोड न लिखें। –

+2

@ ओली, ज़ाहिर है, यह वही है जो मेरा मतलब है "एक खाली शरीर के साथ उपयोगकर्ता द्वारा परिभाषित कन्स्ट्रक्टर के बराबर और एक खाली प्रारंभकर्ता सूची"। मेरा मुद्दा यह है कि यह कहना गलत है कि यह सामान्य मामले में नो-एक्शन कन्स्ट्रक्टर है। –

+0

मेरा [उत्तर] (http://stackoverflow.com/a/36207338/1628638) एक उदाहरण देता है जहां डिफ़ॉल्ट विनाशक स्मृति रिसाव से बचने के लिए महत्वपूर्ण है। –

0

स्मार्ट पॉइंटर्स का उपयोग करते समय, डिफ़ॉल्ट विनाशक (सर्गेई का जवाब देखें) मेमोरी लीक से बचने के लिए महत्वपूर्ण हो सकता है। यहाँ एक उदाहरण:

#include <iostream> 
#include <memory> 

using namespace std; 

class Foo { 
public: 
    Foo(int n = 0): n(n) { cout << "Foo(" << n << ")" << endl; } 
    ~Foo() { cout << "~Foo(" << n << ")" << endl; } 
private: 
    int n; 
}; 

// notes: 
// * default destructor of Bar calls destructors of unique_ptr<Foo> foo 
// and of unique_ptr<Foo[]> foo3, which, in turn, delete the Foo objects 
// * foo2's Foo object leaks 
class Bar { 
public: 
    Bar(): foo(new Foo(1)), foo2(new Foo(2)), foo3(new Foo[2]) { } 
private: 
    unique_ptr<Foo> foo; 
    Foo* foo2; 
    unique_ptr<Foo[]> foo3; 
}; 

int main() { 
    Bar bar; 
    cout << "in main()" << endl; 
} 

यहाँ उत्पादन, दिखा रहा है कि एक रिसाव होता है केवल foo2 के लिए:

Foo(1) 
Foo(2) 
Foo(0) 
Foo(0) 
in main() 
~Foo(0) 
~Foo(0) 
~Foo(1)