आपको वर्चुअल डिस्ट्रक्टर्स का उपयोग कब नहीं करना चाहिए?


99

क्या किसी वर्ग के लिए आभासी विध्वंसक घोषित नहीं करने का कोई अच्छा कारण है ? आपको विशेष रूप से एक लिखने से कब बचना चाहिए?

जवाबों:


72

वर्चुअल विध्वंसक का उपयोग करने की आवश्यकता नहीं है, जब नीचे का कोई भी सच हो:

  • इससे कक्षाएं लगाने का कोई इरादा नहीं है
  • ढेर पर कोई तात्कालिकता नहीं
  • सुपरक्लास के पॉइंटर में स्टोर करने का कोई इरादा नहीं है

इससे बचने का कोई खास कारण नहीं जब तक कि आप वास्तव में याददाश्त के लिए इतने दबाव में न हों।


25
यह एक अच्छा जवाब नहीं है। "कोई ज़रूरत नहीं है" "नहीं करना चाहिए" से अलग है, और "कोई इरादा नहीं" "असंभव बना दिया" से अलग है।
विंडोज प्रोग्रामर

5
इसके अलावा जोड़ें: बेस क्लास पॉइंटर के माध्यम से इंस्टेंट को हटाने का कोई इरादा नहीं है।
एडम रोसेनफील्ड

9
यह वास्तव में इस सवाल का जवाब नहीं देता है। वर्चुअल डोर का उपयोग न करने का आपका अच्छा कारण कहां है?
mxcl

9
मुझे लगता है कि जब कुछ करने की आवश्यकता नहीं है, तो ऐसा न करने का एक अच्छा कारण है। XP के सरल डिजाइन सिद्धांत के बाद।
सितम्बर

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

68

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

C ++ '98 / '03

वर्चुअल डिस्ट्रक्टर जोड़ने से आपकी कक्षा POD (सादा पुराना डेटा) * से बदल सकती है या गैर-POD में एकत्रित हो सकती है। यह आपके प्रोजेक्ट को संकलित करने से रोक सकता है यदि आपका वर्ग प्रकार कहीं आरंभ में एकत्रित है।

struct A {
  // virtual ~A ();
  int i;
  int j;
};
void foo () { 
  A a = { 0, 1 };  // Will fail if virtual dtor declared
}

एक चरम मामले में, इस तरह के बदलाव से अपरिभाषित व्यवहार भी हो सकता है जहां कक्षा का उपयोग एक ऐसे तरीके से किया जा रहा है जिसके लिए एक POD की आवश्यकता होती है, जैसे कि इसे एक दीर्घवृत्तीय पैरामीटर के माध्यम से पारित करना, या इसका उपयोग मेम्की के साथ करना।

void bar (...);
void foo (A & a) { 
  bar (a);  // Undefined behavior if virtual dtor declared
}

[* A POD प्रकार एक प्रकार है जिसकी मेमोरी लेआउट के बारे में विशिष्ट गारंटी होती है। मानक वास्तव में केवल यह कहता है कि यदि आप POD प्रकार के वर्ण (या अहस्ताक्षरित वर्ण) में किसी वस्तु से कॉपी कर सकते हैं और फिर से वापस आ जाएंगे, तो परिणाम मूल वस्तु के समान होगा।]

आधुनिक सी ++

C ++ के हाल के संस्करणों में, POD की अवधारणा को वर्ग लेआउट और इसके निर्माण, प्रतिलिपि बनाने और विनाश के बीच विभाजित किया गया था।

दीर्घवृत्त मामले के लिए, यह अब अपरिभाषित व्यवहार नहीं है जो अब सशर्त रूप से कार्यान्वयन-परिभाषित शब्दार्थ (N3937 - ~ C ++ '14 - 5.2.2 / 7) के साथ समर्थित है:

... एक गैर-तुच्छ प्रतिलिपि निर्माता, एक गैर-तुच्छ चालन निर्माणकर्ता, या एक ही-तुच्छ विध्वंसक, जिसके पास कोई पैरामीटर नहीं है, कार्यान्वयन के साथ सशर्त रूप से समर्थित है, वर्ग प्रकार (खंड 9) के संभावित-मूल्यांकित तर्क को पास करना। परिभाषित शब्दार्थ।

एक विध्वंसक की घोषणा के अलावा =defaultइसका मतलब यह नहीं है कि तुच्छ (12.4 / 5)

... एक विध्वंसक तुच्छ है अगर यह उपयोगकर्ता द्वारा प्रदान नहीं किया गया है ...

मॉडर्न C ++ में अन्य परिवर्तन कुल आरंभिक समस्या के प्रभाव को कम करते हैं क्योंकि एक निर्माणकर्ता को जोड़ा जा सकता है:

struct A {
  A(int i, int j);
  virtual ~A ();
  int i;

  int j;
};
void foo () { 
  A a = { 0, 1 };  // OK
}

1
आप सही हैं, और मैं गलत था, प्रदर्शन एकमात्र कारण नहीं है। लेकिन इससे पता चलता है कि मैं इसके बारे में बाकी था: वर्ग के प्रोग्रामर में बेहतर था कि कोड को शामिल किया जाए ताकि कक्षा को किसी और से विरासत में मिला जा सके।
विंडोज प्रोग्रामर

प्रिय रिचर्ड, क्या आपने जो लिखा है उस पर थोड़ा और टिप्पणी कर सकते हैं। मुझे आपकी बात समझ में नहीं आ रही है, लेकिन यह एकमात्र मूल्यवान बिंदु है जो मैंने गुग्लिंग द्वारा पाया है) या हो सकता है कि आप अधिक विस्तृत विवरण के लिए एक लिंक दे सकते हैं?
जॉन स्मिथ

1
@ जॉनसन मैंने जवाब अपडेट कर दिया है। उम्मीद है कि यह मदद करता है।
रिचर्ड कॉर्डन

28

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


3
और, वास्तव में, जीसीसी पर एक चेतावनी विकल्प है जो ठीक उस मामले पर चेतावनी देता है (आभासी तरीके लेकिन कोई वर्चुअल डोर नहीं)।
सीजरबी

6
यदि आपके पास अन्य वर्चुअल फ़ंक्शंस हैं, तो चाहे आप कक्षा से प्राप्त करते हों, स्मृति को लीक करने का जोखिम नहीं उठाते हैं?
मेग रोडर

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

7

जब भी कोई मौका मिलता है तो एक वर्चुअल विध्वंसक की आवश्यकता होती है deleteजो आपके वर्ग के प्रकार के साथ एक उपवर्ग के ऑब्जेक्ट के लिए पॉइंटर पर बुलाया जा सकता है। यह सुनिश्चित करता है कि सही डिस्ट्रक्टर को कंपाइलर समय पर ढेर पर किसी वस्तु के वर्ग को जानने के लिए संकलक के बिना रन टाइम पर बुलाया जाता है। उदाहरण के लिए, मान Bलेना एक उपवर्ग है A:

A *x = new B;
delete x;     // ~B() called, even though x has type A*

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

हालाँकि, यदि आप अपने आप deleteको एक तंग पाश में बहुत सी वस्तुओं को मिलाते हैं, तो आभासी फ़ंक्शन (यहां तक ​​कि खाली है) को कॉल करने का प्रदर्शन ओवरहेड हो सकता है। कंपाइलर आमतौर पर इन कॉल्स को इनलाइन नहीं कर सकता है, और प्रोसेसर को यह अनुमान लगाने में मुश्किल समय हो सकता है कि कहां जाना है। यह संभावना नहीं है कि यह प्रदर्शन पर एक महत्वपूर्ण प्रभाव पड़ेगा, लेकिन यह ध्यान देने योग्य है।


"यदि आपका कोड प्रदर्शन के लिए महत्वपूर्ण नहीं है, तो आपके द्वारा लिखे गए प्रत्येक आधार वर्ग में वर्चुअल विध्वंसक जोड़ना उचित होगा, बस सुरक्षा के लिए।" मेरे द्वारा देखे जाने वाले प्रत्येक उत्तर में अधिक जोर दिया जाना चाहिए
csguy

5

वर्चुअल फ़ंक्शंस का अर्थ है कि प्रत्येक आवंटित ऑब्जेक्ट वर्चुअल फ़ंक्शन टेबल पॉइंटर द्वारा मेमोरी लागत में वृद्धि करता है।

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

अन्य सभी मामलों में, आप अपने आप को डीओआरटी आभासी बनाने के लिए डिबग दुख से बचाएंगे।


1
बस नाइट-पिकिंग, लेकिन इन दिनों एक पॉइंटर अक्सर 32 के बजाय 64 बिट्स होगा।
हेड गीक

5

सभी सी ++ कक्षाएं गतिशील बहुरूपता के साथ आधार वर्ग के रूप में उपयोग करने के लिए उपयुक्त नहीं हैं।

यदि आप चाहते हैं कि आपकी कक्षा गतिशील बहुरूपता के लिए उपयुक्त हो, तो इसका विध्वंसक आभासी होना चाहिए। इसके अलावा, कोई भी विधियाँ, जो उप-वर्ग की अवधारणा को ओवरराइड करना चाहती हो सकती है (जिसका अर्थ सभी सार्वजनिक विधियाँ हो सकती हैं, साथ ही संभवतः आंतरिक रूप से उपयोग किए जाने वाले कुछ संरक्षित) आभासी होने चाहिए।

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

यहां एक वर्ग का उदाहरण दिया गया है जो गतिशील बहुरूपता के लिए उपयुक्त नहीं होगा, भले ही इसका विध्वंसक आभासी हो:

class MutexLock {
    mutex *mtx_;
public:
    explicit MutexLock(mutex *mtx) : mtx_(mtx) { mtx_->lock(); }
    ~MutexLock() { mtx_->unlock(); }
private:
    MutexLock(const MutexLock &rhs);
    MutexLock &operator=(const MutexLock &rhs);
};

इस कक्षा का पूरा बिंदु RAII के लिए स्टैक पर बैठना है। यदि आप इस वर्ग की वस्तुओं की ओर संकेत कर रहे हैं, तो इसे उपवर्गों में जाने दें, फिर आप इसे गलत कर रहे हैं।


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

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

4

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

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

ठीक है, एक एकल अपवाद है, अर्थात् यदि आपकी कक्षा है (गलत-) का उपयोग व्युत्पन्न वस्तुओं के बहुरूपता को हटाने के लिए किया जाता है, लेकिन तब आप या अन्य लोग - उम्मीद है कि यह जानते हैं कि इसके लिए एक वर्चुअल विध्वंसक की आवश्यकता है।

एक और तरीका रखो, अगर आपकी कक्षा में एक गैर-आभासी विध्वंसक है तो यह एक बहुत ही स्पष्ट कथन है: "व्युत्पन्न वस्तुओं को हटाने के लिए मेरा उपयोग न करें!"


3

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


1

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

ध्यान दें कि वर्चुअल टेबल लुकअप किसी ऑब्जेक्ट के लिए होता है यदि उस ऑब्जेक्ट पर कोई भी तरीका वर्चुअल है। यदि क्लास में अन्य वर्चुअल तरीके हैं, तो विध्वंसक पर वर्चुअल स्पेसिफिकेशन निकालने का कोई मतलब नहीं है।


1

यदि आप पूरी तरह से सकारात्मक रूप से यह सुनिश्चित करते हैं कि आपकी कक्षा में एक व्यवहार्य नहीं है, तो आपके पास एक आभासी विध्वंसक भी नहीं होना चाहिए।

यह एक दुर्लभ मामला है, लेकिन ऐसा होता है।

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


0

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

यदि आप वर्ग से प्राप्त करने का इरादा नहीं रखते हैं, तो विध्वंसक को आभासी होने की कोई आवश्यकता नहीं है। और यहां तक ​​कि अगर आप करते हैं, तो एक संरक्षित गैर-वर्चुअल विध्वंसक बस उतना ही अच्छा है अगर आधार वर्ग के संकेत को हटाने की आवश्यकता नहीं है


-7

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


बहुरूपता निश्चित रूप से चीजों को धीमा कर देगा। इसकी तुलना एक ऐसी स्थिति से करें, जहाँ हमें बहुरूपता की जरूरत है और न चुनने की, यह और भी धीमी होगी। उदाहरण: हम आरटीटीआई और संसाधनों को साफ करने के लिए एक स्विच स्टेटमेंट का उपयोग करके बेस क्लास डिस्ट्रक्टर में सभी तर्क को लागू करते हैं।
sep

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

1
... बस विध्वंसक को आभासी बनाने का मतलब यह नहीं है कि वर्ग अनिवार्य रूप से आधार वर्ग के रूप में सही ढंग से काम करेगा। इसलिए इसे "सिर्फ इसलिए" वर्चुअल चिह्नित करना, उस मूल्यांकन को करने के बजाय, एक चेक लिख रहा है मेरा कोड नकद नहीं कर सकता है।
स्टीव जेसप
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.