क्या किसी वर्ग के लिए आभासी विध्वंसक घोषित नहीं करने का कोई अच्छा कारण है ? आपको विशेष रूप से एक लिखने से कब बचना चाहिए?
जवाबों:
वर्चुअल विध्वंसक का उपयोग करने की आवश्यकता नहीं है, जब नीचे का कोई भी सच हो:
इससे बचने का कोई खास कारण नहीं जब तक कि आप वास्तव में याददाश्त के लिए इतने दबाव में न हों।
स्पष्ट रूप से प्रश्न का उत्तर देने के लिए, जब आपको वर्चुअल विध्वंसक घोषित नहीं करना चाहिए ।
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
}
मैं एक आभासी विध्वंसक घोषित करता हूं अगर और केवल अगर मेरे पास आभासी विधियां हैं। एक बार जब मेरे पास आभासी विधियां होती हैं, तो मैं खुद पर भरोसा नहीं करता कि इसे ढेर पर लगाने या बेस क्लास के लिए पॉइंटर स्टोर करने से बचने के लिए। ये दोनों बेहद सामान्य ऑपरेशन हैं और अक्सर विध्वंसक रूप से संसाधनों को लीक कर देंगे यदि विध्वंसक को आभासी घोषित नहीं किया जाता है।
जब भी कोई मौका मिलता है तो एक वर्चुअल विध्वंसक की आवश्यकता होती है delete
जो आपके वर्ग के प्रकार के साथ एक उपवर्ग के ऑब्जेक्ट के लिए पॉइंटर पर बुलाया जा सकता है। यह सुनिश्चित करता है कि सही डिस्ट्रक्टर को कंपाइलर समय पर ढेर पर किसी वस्तु के वर्ग को जानने के लिए संकलक के बिना रन टाइम पर बुलाया जाता है। उदाहरण के लिए, मान B
लेना एक उपवर्ग है A
:
A *x = new B;
delete x; // ~B() called, even though x has type A*
यदि आपका कोड महत्वपूर्ण प्रदर्शन नहीं करता है, तो आपके द्वारा लिखे गए प्रत्येक आधार वर्ग में एक वर्चुअल डिस्ट्रक्टर जोड़ना उचित होगा, बस सुरक्षा के लिए।
हालाँकि, यदि आप अपने आप delete
को एक तंग पाश में बहुत सी वस्तुओं को मिलाते हैं, तो आभासी फ़ंक्शन (यहां तक कि खाली है) को कॉल करने का प्रदर्शन ओवरहेड हो सकता है। कंपाइलर आमतौर पर इन कॉल्स को इनलाइन नहीं कर सकता है, और प्रोसेसर को यह अनुमान लगाने में मुश्किल समय हो सकता है कि कहां जाना है। यह संभावना नहीं है कि यह प्रदर्शन पर एक महत्वपूर्ण प्रभाव पड़ेगा, लेकिन यह ध्यान देने योग्य है।
वर्चुअल फ़ंक्शंस का अर्थ है कि प्रत्येक आवंटित ऑब्जेक्ट वर्चुअल फ़ंक्शन टेबल पॉइंटर द्वारा मेमोरी लागत में वृद्धि करता है।
इसलिए यदि आपके कार्यक्रम में किसी बड़ी संख्या में किसी वस्तु को आवंटित करना शामिल है, तो अतिरिक्त 32 बिट्स को प्रत्येक ऑब्जेक्ट को सहेजने के लिए सभी वर्चुअल फ़ंक्शंस से बचने के लायक होगा।
अन्य सभी मामलों में, आप अपने आप को डीओआरटी आभासी बनाने के लिए डिबग दुख से बचाएंगे।
सभी सी ++ कक्षाएं गतिशील बहुरूपता के साथ आधार वर्ग के रूप में उपयोग करने के लिए उपयुक्त नहीं हैं।
यदि आप चाहते हैं कि आपकी कक्षा गतिशील बहुरूपता के लिए उपयुक्त हो, तो इसका विध्वंसक आभासी होना चाहिए। इसके अलावा, कोई भी विधियाँ, जो उप-वर्ग की अवधारणा को ओवरराइड करना चाहती हो सकती है (जिसका अर्थ सभी सार्वजनिक विधियाँ हो सकती हैं, साथ ही संभवतः आंतरिक रूप से उपयोग किए जाने वाले कुछ संरक्षित) आभासी होने चाहिए।
यदि आपकी कक्षा गतिशील बहुरूपता के लिए उपयुक्त नहीं है, तो विध्वंसक को आभासी के रूप में चिह्नित नहीं किया जाना चाहिए, क्योंकि ऐसा करना गलत है। यह सिर्फ लोगों को आपकी कक्षा का गलत तरीके से उपयोग करने के लिए प्रोत्साहित करता है।
यहां एक वर्ग का उदाहरण दिया गया है जो गतिशील बहुरूपता के लिए उपयुक्त नहीं होगा, भले ही इसका विध्वंसक आभासी हो:
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 के लिए स्टैक पर बैठना है। यदि आप इस वर्ग की वस्तुओं की ओर संकेत कर रहे हैं, तो इसे उपवर्गों में जाने दें, फिर आप इसे गलत कर रहे हैं।
विध्वंसक को आभासी घोषित न करने का एक अच्छा कारण यह है कि जब यह आपकी कक्षा को वर्चुअल फ़ंक्शन तालिका जोड़े जाने से बचाता है, और आपको जब भी संभव हो, इससे बचना चाहिए।
मुझे पता है कि बहुत से लोग हमेशा विध्वंसक को आभासी घोषित करना पसंद करते हैं, सिर्फ सुरक्षित पक्ष में होना। लेकिन अगर आपकी कक्षा में कोई अन्य वर्चुअल फ़ंक्शन नहीं है, तो वास्तव में, वर्चुअल विध्वंसक होने का कोई मतलब नहीं है। यहां तक कि अगर आप अपनी कक्षा अन्य लोगों को देते हैं, जो तब उससे अन्य कक्षाएं प्राप्त करते हैं, तो उनके पास कभी भी एक पॉइंटर पर डिलीट को कॉल करने का कोई कारण नहीं होगा जो आपकी कक्षा तक था - और यदि वे ऐसा करते हैं तो मैं इसे एक बग मानूंगा।
ठीक है, एक एकल अपवाद है, अर्थात् यदि आपकी कक्षा है (गलत-) का उपयोग व्युत्पन्न वस्तुओं के बहुरूपता को हटाने के लिए किया जाता है, लेकिन तब आप या अन्य लोग - उम्मीद है कि यह जानते हैं कि इसके लिए एक वर्चुअल विध्वंसक की आवश्यकता है।
एक और तरीका रखो, अगर आपकी कक्षा में एक गैर-आभासी विध्वंसक है तो यह एक बहुत ही स्पष्ट कथन है: "व्युत्पन्न वस्तुओं को हटाने के लिए मेरा उपयोग न करें!"
यदि आपके पास बहुत अधिक संख्या में उदाहरणों के साथ बहुत छोटा वर्ग है, तो एक वाइबेटर पॉइंटर का ओवरहेड आपके प्रोग्राम की मेमोरी उपयोग में अंतर ला सकता है। जब तक आपकी कक्षा के पास कोई अन्य आभासी विधियां नहीं हैं, तब तक विध्वंसक गैर-आभासी बनाने से उस ओवरहेड को बचाया जा सकेगा।
मैं आमतौर पर विध्वंसक आभासी घोषित करता हूं, लेकिन अगर आपके पास प्रदर्शन महत्वपूर्ण कोड है जो एक आंतरिक लूप में उपयोग किया जाता है, तो आप वर्चुअल टेबल लुकअप से बचना चाह सकते हैं। यह कुछ मामलों में महत्वपूर्ण हो सकता है, जैसे टकराव की जाँच। लेकिन सावधानी बरतें कि आप उन वस्तुओं को कैसे नष्ट करते हैं यदि आप विरासत का उपयोग करते हैं, या आप वस्तु का केवल आधा हिस्सा नष्ट कर देंगे।
ध्यान दें कि वर्चुअल टेबल लुकअप किसी ऑब्जेक्ट के लिए होता है यदि उस ऑब्जेक्ट पर कोई भी तरीका वर्चुअल है। यदि क्लास में अन्य वर्चुअल तरीके हैं, तो विध्वंसक पर वर्चुअल स्पेसिफिकेशन निकालने का कोई मतलब नहीं है।
यदि आप पूरी तरह से सकारात्मक रूप से यह सुनिश्चित करते हैं कि आपकी कक्षा में एक व्यवहार्य नहीं है, तो आपके पास एक आभासी विध्वंसक भी नहीं होना चाहिए।
यह एक दुर्लभ मामला है, लेकिन ऐसा होता है।
एक पैटर्न का सबसे परिचित उदाहरण जो ऐसा करता है वह है डायरेक्टएक्स डी 3 डीएक्टएक्टर और डी 3 डीएमएआरटीआईआर कक्षाएं। सिंटैक्टिक शुगर के लिए फ़ंक्शंस के बजाय ये क्लास मेथड हैं, लेकिन फ़ंक्शन ओवरहेड से बचने के लिए जानबूझकर क्लास में वाइबेट नहीं होता है क्योंकि ये क्लास ख़ासकर कई हाई-परफॉर्मेंस एप्लिकेशन के इनर लूप में इस्तेमाल होती हैं।
ऑपरेशन पर जो बेस क्लास पर किया जाएगा, और जो वस्तुतः व्यवहार करना चाहिए, आभासी होना चाहिए। यदि बेस क्लास इंटरफ़ेस के माध्यम से विलोपन पॉलीमॉर्फिक रूप से किया जा सकता है, तो इसे वस्तुतः व्यवहार करना होगा और आभासी होना चाहिए।
यदि आप वर्ग से प्राप्त करने का इरादा नहीं रखते हैं, तो विध्वंसक को आभासी होने की कोई आवश्यकता नहीं है। और यहां तक कि अगर आप करते हैं, तो एक संरक्षित गैर-वर्चुअल विध्वंसक बस उतना ही अच्छा है अगर आधार वर्ग के संकेत को हटाने की आवश्यकता नहीं है ।
प्रदर्शन का उत्तर केवल वही है जिसके बारे में मुझे पता है कि सच होने की संभावना है। यदि आपने मापा है और पाया है कि आपके विनाशकर्ताओं को डी-वर्चुअलाइज़ करना वास्तव में चीजों को गति देता है, तो आपको शायद उस वर्ग की अन्य चीजें मिल गई हैं, जिन्हें गति देने की भी आवश्यकता है, लेकिन इस बिंदु पर अधिक महत्वपूर्ण विचार हैं। किसी दिन किसी को पता चलता है कि आपका कोड उनके लिए एक अच्छा आधार वर्ग प्रदान करेगा और उन्हें एक सप्ताह का काम बचाएगा। आप अपने कोड को आधार के रूप में उपयोग करने के बजाय, सुनिश्चित करें कि वे उस सप्ताह का काम करें, अपने कोड को कॉपी और पेस्ट करें। आप बेहतर यह सुनिश्चित करेंगे कि आप अपने कुछ महत्वपूर्ण तरीकों को निजी बनाएं ताकि कोई भी आपसे कभी विरासत में न मिले।