जवाबों:
यह एक इंटरफेस के लिए और भी महत्वपूर्ण है। आपकी कक्षा का कोई भी उपयोगकर्ता संभवतः इंटरफ़ेस को पॉइंटर रखेगा, न कि कंक्रीट कार्यान्वयन के लिए पॉइंटर। जब वे इसे हटाने के लिए आते हैं, यदि विध्वंसक गैर-आभासी होता है, तो वे इंटरफ़ेस के विध्वंसक (या संकलक-प्रदान की गई डिफ़ॉल्ट, यदि आप एक को निर्दिष्ट नहीं करते हैं) को कॉल करेंगे, न कि व्युत्पन्न वर्ग के विध्वंसक को। तुरंत स्मृति रिसाव।
उदाहरण के लिए
class Interface
{
virtual void doSomething() = 0;
};
class Derived : public Interface
{
Derived();
~Derived()
{
// Do some important cleanup...
}
};
void myFunc(void)
{
Interface* p = new Derived();
// The behaviour of the next line is undefined. It probably
// calls Interface::~Interface, not Derived::~Derived
delete p;
}
[expr.delete]/
: ... if the static type of the object to be deleted is different from its dynamic type, ... the static type shall have a virtual destructor or the behavior is undefined. ...
। यह अभी भी अपरिभाषित होगा यदि व्युत्पन्न ने एक अंतर्निहित उत्पन्न विध्वंसक का उपयोग किया।
आपके प्रश्न का उत्तर अक्सर होता है, लेकिन हमेशा नहीं। यदि आपका सार वर्ग क्लाइंट को एक पॉइंटर को डिलीट करने के लिए मना करता है (या यदि वह इसके प्रलेखन में ऐसा कहता है), तो आप वर्चुअल डिस्ट्रक्टर घोषित नहीं करने के लिए स्वतंत्र हैं।
आप अपने डिस्ट्रॉयर को सुरक्षित बनाकर ग्राहकों को एक पॉइंटर पर डिलीट करने के लिए मना कर सकते हैं। इस तरह से काम करना, एक आभासी विध्वंसक को छोड़ना पूरी तरह से सुरक्षित और उचित है।
आप अंततः बिना किसी वर्चुअल पद्धति तालिका के साथ समाप्त हो जाएंगे, और अपने ग्राहकों को अपने सूचक को इसे एक सूचक के माध्यम से गैर-डिलीट करने योग्य बनाने पर संकेत देंगे, इसलिए आपके पास वास्तव में उन मामलों में इसे आभासी घोषित नहीं करने का कारण है।
[इस लेख में आइटम ४ देखें: http://www.gotw.ca/publications/mill18.htm ]
मैंने कुछ शोध करने का निर्णय लिया और आपके उत्तरों को संक्षेप में बताने का प्रयास किया। निम्नलिखित प्रश्न आपको यह तय करने में मदद करेंगे कि आपको किस प्रकार के विध्वंसक की आवश्यकता है:
आशा है कि ये आपकी मदद करेगा।
* यह ध्यान रखना महत्वपूर्ण है कि सी + + में क्लास को अंतिम (यानी नॉन सबक्लेसेबल) के रूप में चिह्नित करने का कोई तरीका नहीं है, इसलिए यदि आप अपने विध्वंसक को गैर-आभासी और सार्वजनिक घोषित करने का निर्णय लेते हैं, तो अपने साथी प्रोग्रामरों के खिलाफ स्पष्ट रूप से चेतावनी देना याद रखें अपनी कक्षा से निकलना।
संदर्भ:
हाँ यह हमेशा महत्वपूर्ण है। व्युत्पन्न कक्षाएं स्मृति को आवंटित कर सकती हैं या अन्य संसाधनों के संदर्भ को रोक सकती हैं जिन्हें ऑब्जेक्ट के नष्ट होने पर साफ करना होगा। यदि आप अपने इंटरफेस / एब्स्ट्रैक्ट क्लासेस को वर्चुअल डिस्ट्रक्टर्स नहीं देते हैं, तो हर बार जब आप बेस क्लास के माध्यम से एक व्युत्पन्न क्लास इंस्टेंस को हटाते हैं, तो आपके व्युत्पन्न क्लास के डिस्ट्रक्टर को नहीं बुलाया जाएगा।
इसलिए, आप मेमोरी लीक की संभावना को खोल रहे हैं
class IFoo
{
public:
virtual void DoFoo() = 0;
};
class Bar : public IFoo
{
char* dooby = NULL;
public:
virtual void DoFoo() { dooby = new char[10]; }
void ~Bar() { delete [] dooby; }
};
IFoo* baz = new Bar();
baz->DoFoo();
delete baz; // memory leak - dooby isn't deleted
इसकी हमेशा आवश्यकता नहीं होती है, लेकिन मुझे यह अच्छा अभ्यास लगता है। यह क्या करता है, क्या यह किसी व्युत्पन्न वस्तु को आधार प्रकार के पॉइंटर के माध्यम से सुरक्षित रूप से हटाने की अनुमति देता है।
उदाहरण के लिए:
Base *p = new Derived;
// use p as you see fit
delete p;
यदि Base
एक आभासी विध्वंसक नहीं है, तो यह बीमार है क्योंकि यह ऑब्जेक्ट को हटाने का प्रयास करेगा जैसे कि यह एक था Base *
।
shared_ptr
ऑब्जेक्ट को हटाने का प्रयास करेगा जैसे कि यह एक था Base *
- यह उस चीज़ का प्रकार याद रखता है जिसे आपने इसे बनाया था। संदर्भित लिंक देखें, विशेष रूप से बिट जो कहता है "विध्वंसक उसी सूचक के साथ डिलीट कॉल करेगा, अपने मूल प्रकार के साथ पूर्ण, तब भी जब टी में वर्चुअल विध्वंसक नहीं है, या शून्य है।"
यह केवल अच्छा अभ्यास नहीं है। यह किसी भी वर्ग पदानुक्रम के लिए नियम # 1 है।
अब क्यों के लिए। ठेठ जानवर पदानुक्रम ले लो। वर्चुअल डिस्ट्रक्टर्स वर्चुअल डिस्पैच के माध्यम से किसी अन्य विधि कॉल के रूप में जाते हैं। निम्नलिखित उदाहरण लें।
Animal* pAnimal = GetAnimal();
delete pAnimal;
मान लें कि पशु एक अमूर्त वर्ग है। एक ही तरीका है कि C ++ को कॉल करने के लिए उचित विध्वंसक पता है कि आभासी विधि प्रेषण के माध्यम से है। यदि विध्वंसक आभासी नहीं है, तो यह बस पशु के विनाशकर्ता को बुलाएगा और व्युत्पन्न वर्गों में किसी भी वस्तु को नष्ट नहीं करेगा।
बेस क्लास में विध्वंसक को आभासी बनाने का कारण यह है कि यह केवल व्युत्पन्न वर्गों से विकल्प को हटाता है। उनका विध्वंसक डिफ़ॉल्ट रूप से आभासी हो जाता है।
उत्तर सरल है, आपको इसे आभासी होने की आवश्यकता है अन्यथा आधार वर्ग एक पूर्ण बहुरूपी वर्ग नहीं होगा।
Base *ptr = new Derived();
delete ptr; // Here the call order of destructors: first Derived then Base.
आप उपरोक्त विलोपन को पसंद करेंगे, लेकिन यदि बेस क्लास का विध्वंसक आभासी नहीं है, तो केवल बेस क्लास के विध्वंसक को बुलाया जाएगा और व्युत्पन्न वर्ग के सभी डेटा अनिर्धारित रहेंगे।
delete p
अपरिभाषित व्यवहार करता है। यह कॉल करने की गारंटी नहीं हैInterface::~Interface
।