2009-07-14 16 views
38

संभव डुप्लिकेट:
When to use virtual destructors?आपका विनाशक कब आभासी होना चाहिए?

जब आपके सी ++ वस्तु नाशक virtual होना चाहिए?

+2

से कॉपी किया गया? – Stobor

+0

संबंधित सामग्री के बहुत सारे: http://stackoverflow.com/search?q=virtual+destructor –

+0

इस लिंक को आजमाएं http://stackoverflow.com/questions/461203/when-to-use-virtual-destructors/15903538#15903538 यह –

उत्तर

47
  1. आप आभासी नाशक की जरूरत है जब वर्ग तरीकों में से कम से कम में एक आभासी है।

ऐसा इसलिए है क्योंकि वर्चुअल विधि का कारण यह है कि आप बहुरूपता का उपयोग करना चाहते हैं। मतलब आप बेस क्लास पॉइंटर पर एक विधि कॉल करेंगे और आप सबसे व्युत्पन्न कार्यान्वयन चाहते हैं - यह बहुरूपता का पूरा बिंदु है।

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

class A 
{ 
    virtual void f() {} 
    ~A() {} 
} 

class B : public A 
{ 
    void f() {} 
    ~B() {} 
} 

A * thing = new B(); 
thing->f(); // calls B's f() 
delete thing; // calls ~A(), not what you wanted, you wanted ~B() 

होने ~ एक() बहुरूपता

virtual ~A() {} 

पर आभासी बदल जाता है तो जब आप अब फोन

delete thing; 

~ बी() कहा जाएगा।

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

आप देख सकते हैं कि एसटीएल कक्षाओं में वर्चुअल विनाशक नहीं हैं इसलिए उन्हें विस्तारित नहीं किया जाना चाहिए (उदा। Std :: vector, std :: string ...)। यदि आप std :: vector का विस्तार करते हैं और आप पॉइंटर या संदर्भ के माध्यम से बेस क्लास पर विनाशक को कॉल करते हैं तो आप निश्चित रूप से अपने विशेष श्रेणी विनाशक को कॉल नहीं करेंगे जो स्मृति रिसाव का कारण बन सकता है।

31

Stroustrup's C++ Style and Technique FAQ से:

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

when your destructor should be virtual on the C++ FAQ पर अतिरिक्त जानकारी के बहुत सारे। (धन्यवाद Stobor)

आभासी सदस्य क्या है? C++ FAQ से:

[20.1] "वर्चुअल सदस्य फ़ंक्शन" क्या है?

ओओ परिप्रेक्ष्य से, यह सी ++: [6.9], [6.10] की एकल सबसे महत्वपूर्ण विशेषता है।

एक वर्चुअल फ़ंक्शन बेस क्लास द्वारा प्रदान किए गए कार्यान्वयन को प्रतिस्थापित करने के लिए कक्षाओं को प्राप्त करने की अनुमति देता है। संकलक यकीन है कि प्रतिस्थापन हमेशा कहा जाता है कि जब भी प्रश्न में वस्तु व्युत्पन्न वर्ग की वास्तव में है, भले ही वस्तु एक व्युत्पन्न सूचक एक आधार सूचक के बजाय द्वारा पहुँचा जा सकता है बनाता है। यह बेस क्लास में एल्गोरिदम व्युत्पन्न कक्षा में प्रतिस्थापित होने की अनुमति देता है, भले ही उपयोगकर्ता व्युत्पन्न कक्षा के बारे में नहीं जानते हैं।

व्युत्पन्न वर्ग या तो पूरी तरह जगह ले सकता है ("ओवरराइड") आधार वर्ग सदस्य समारोह, या व्युत्पन्न वर्ग आंशिक रूप से ("बढ़ाने") आधार वर्ग सदस्य समारोह जगह ले सकता है। उत्तरार्द्ध व्युत्पन्न क्लास सदस्य फ़ंक्शन द्वारा प्राप्त किया गया है, यदि इच्छित हो तो बेस कक्षा सदस्य फ़ंक्शन को कॉल करें।

+0

की मदद कर सकता है, यह एकमात्र मामला नहीं है, हालांकि ... – Stobor

+0

+1 श्री एस से अच्छी प्रतिलिपि पेस्ट उद्धरण, वास्तव में उस उत्तर को हरा नहीं सकता – stefanB

+2

सटीक होने के लिए, जब माता-पिता के बच्चे वर्ग के साथ नहीं आभासी कार्य एक ऐसे सदस्य को परिभाषित करता है जिसके लिए सफाई की आवश्यकता होती है, लेकिन जो माता-पिता में निहित नहीं है? आभासी विनाशक की कमी का मतलब है "अभिभावक को हटाएं" बच्चे वर्ग के विनाशक को नहीं बुलाएगा ... – Stobor

-1

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

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

0

हमेशा।

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

+1

सी ++ ने आपको वह लचीलापन नहीं दिया है ताकि आप इसे फेंक सकें। एक और तरीका रखो, "जब तक मैं वास्तव में एक vtable के भंडारण और प्रदर्शन ओवरहेड से चिंतित नहीं हूं, मैं पाइथन या लुआ जैसे एक आसान भाषा का उपयोग करने जा रहा हूं।" – Tom

+1

"सी पैर में खुद को शूट करना आसान बनाता है; सी ++ इसे कठिन बनाता है, लेकिन जब आप ऐसा करते हैं तो यह आपके पूरे पैर को उड़ाता है" - स्ट्रास्ट्रुप। सी ++ सही स्थिति में एक बेहद उपयोगी भाषा है, लेकिन आपको खुद को सुरक्षित रखना है। या तो इसे हमेशा वर्चुअल बनाएं, या स्वयं को बचाने के लिए एक स्थिर विश्लेषण टूल ढूंढें, या जब कोई आपका कोड बदलता है तो प्रत्येक विनाशक की मैन्युअल रूप से समीक्षा करें। –

+0

@ जेरेड: या समझदार नियमों और दस्तावेजों के साधनों को प्रस्तुत करना जो कक्षाओं के आधार पर कक्षाओं का उपयोग किया जा सकता है, और कैसे। आपको प्रत्येक कोड परिवर्तन के लिए विनाशक की समीक्षा करने की आवश्यकता नहीं है, केवल उन परिवर्तनों के लिए जो कक्षा की बहुरूपता विशेषताओं को बदलते हैं (कोई भी/स्थिर/गतिशील)। उस ने कहा, यदि आप सबकुछ के लिए गतिशील बहुरूपता का उपयोग करते हैं, तो कक्षाओं को विरासत-तैयार करना निश्चित रूप से आसान है जब तक कि अन्यथा साबित न हो जाए। एक्स-जावा प्रोग्रामर को शायद पूर्व-सी प्रोग्रामर की तुलना में अधिक आभासी विनाशकों और विधियों की आवश्यकता है, इसलिए मुझे लगता है कि "डिफ़ॉल्ट रूप से आभासी" का चयन कर सकते हैं। –

3

यदि आप बेस क्लास पॉइंटर के माध्यम से व्युत्पन्न कक्षा की वस्तुओं को नष्ट कर देंगे (या यहां तक ​​कि शायद), तो आपको एक आभासी विनाशक की आवश्यकता होगी।

मैं दृष्टिकोण लेता हूं कि यदि मैं कक्षा में सभी से प्राप्त करने जा रहा हूं, तो यह एक आभासी विनाशक होगा। मेरे द्वारा लिखे गए कोड में प्रभावी रूप से कोई मामला नहीं है, जहां आभासी विनाशक पदार्थ के प्रदर्शन प्रभाव पड़ते हैं, और यहां तक ​​कि यदि आज वास्तव में इसकी आवश्यकता नहीं है, तो कक्षा में संशोधित होने पर भविष्य में इसकी आवश्यकता समाप्त हो सकती है।

असल में: सभी बेस क्लास विनाशकों पर वर्चुअल रखें जब तक कि आपके पास अच्छा, अच्छी तरह से विचार न हो।

यह अंगूठे का एक और नियम है, लेकिन यह वह है जो आपको बाद में गलतियों से दूर रखता है।

5

मैं हाल ही में यह निष्कर्ष निकला कि पूरी तरह से सही जवाब यह है आए हैं:

दिशानिर्देश # 4: एक आधार वर्ग नाशक या तो सार्वजनिक और आभासी, या संरक्षित और nonvirtual होना चाहिए।

और निश्चित रूप से Herb Sutter gives the rationale अपने दावे के लिए। ध्यान दें कि वह सामान्य उत्तरों से आगे जाता है "जब कोई बेस-क्लास पॉइंटर के माध्यम से व्युत्पन्न-वर्ग ऑब्जेक्ट को हटा देगा" और "यदि आपके वर्ग में कोई वर्चुअल फ़ंक्शन है तो" अपने विनाशक वर्चुअल बनाएं "।

+1

मैं इसे उन 2 विकल्पों तक सीमित नहीं करूँगा। आप इस मामले में प्रोग्रामिंग भाषा की विशेषताओं के निर्माण के लिए टूल का उपयोग करते हैं। यदि आप हर सार्वजनिक विनाशक सार्वजनिक बनाते हैं तो आप उन वर्गों में से प्रत्येक के लिए बहुरूपता को चालू करते हैं, शायद उन 9 0% मामलों में आपको इसकी आवश्यकता नहीं होती है और आप अनावश्यक ओवरहेड के साथ समाप्त होते हैं। – stefanB

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