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


48

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

तो सवाल यह है: क्यों सी + + डिफ़ॉल्ट रूप से सभी विध्वंसक आभासी सेट नहीं करता है? या अन्य प्रश्नों में:

मुझे वर्चुअल विध्वंसक का उपयोग करने की आवश्यकता नहीं है?

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

आभासी विध्वंसक का उपयोग करने की लागत क्या है अगर मैं इसका उपयोग करता हूं, भले ही इसकी आवश्यकता न हो?


6
और क्या होगा अगर आपकी कक्षा विरासत में नहीं मिली है? कई मानक पुस्तकालय कक्षाओं को देखें, कुछ में वर्चुअल फ़ंक्शंस होते हैं क्योंकि वे विरासत में प्राप्त नहीं किए गए हैं।
कुछ प्रोग्रामर ने

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

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

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

2
@underscore_d "पॉइंटर" लाल हेरिंग लगता है। यह संभवतः सदस्य के लिए एक संकेतक होना चाहिए (जो परिभाषा से सूचक नहीं है)। सामान्य ABI के साथ, एक पॉइंटर को सदस्य को अक्सर मशीन शब्द (विशिष्ट बिंदुओं के रूप में) में फिट नहीं किया जाता है, और गैर-पॉलीमॉर्फ़िक से पॉलीमोर्फिक तक एक वर्ग को बदलने से अक्सर पॉइंटर का आकार इस वर्ग के सदस्य में बदल जाता है।
फ्रैंकएचबी

जवाबों:


41

यदि आप एक वर्ग में एक आभासी विध्वंसक जोड़ते हैं:

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

  • वर्चुअल डिस्पैच टेबल का पता आवश्यक रूप से प्रक्रियाओं में मान्य नहीं है, जो साझा मेमोरी में ऐसी वस्तुओं को सुरक्षित रूप से साझा करने से रोक सकता है

  • एक एम्बेडेड वर्चुअल पॉइंटर है जो कुछ ज्ञात इनपुट या आउटपुट प्रारूप से मेल खाते मेमोरी लेआउट के साथ एक वर्ग बनाता है (उदाहरण के लिए, इसलिए एक Price_Tick*आने वाले यूडीपी पैकेट में सीधे संरेखित मेमोरी के उद्देश्य से हो सकता है और डेटा को पार्स / एक्सेस या बदलने या उपयोग करने के लिए उपयोग किया जा सकता है) प्लेसमेंट- newऐसे वर्ग को एक आउटगोइंग पैकेट में डेटा लिखने के लिए)

  • विध्वंसक खुद कॉल कर सकता है - कुछ शर्तों के तहत - वस्तुतः और इसलिए आउट-ऑफ-लाइन भेजना पड़ता है, जबकि गैर-वर्चुअल विध्वंसक कॉलिंग के लिए तुच्छ या अप्रासंगिक होने पर दूर या अनुकूलित हो सकता है

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

"ज्यादातर मामलों में विध्वंसक को आभासी होने की आवश्यकता होती है"

ऐसा नहीं ... कई वर्गों को ऐसी कोई आवश्यकता नहीं है। ऐसे कई उदाहरण हैं जहां यह अनावश्यक है उन्हें मूर्खतापूर्ण लगता है, लेकिन बस अपने मानक पुस्तकालय के माध्यम से देखें या कहें कि बढ़ावा दें और आप देखेंगे कि बड़ी संख्या में ऐसे वर्ग हैं जिनमें वर्चुअल विध्वंसक नहीं हैं। 1.53 को बढ़ावा देने में, मैं 494 में से 72 आभासी विध्वंसक गिनता हूं।


23

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

  1. एक ठोस वर्ग के लिए जो विरासत में नहीं आना चाहता।
  2. पॉलीमोर्फिक विलोपन के बिना एक आधार वर्ग के लिए। बेस के लिए पॉइंटर का उपयोग करके या तो क्लाइंट पॉलीमॉर्फिक रूप से हटाने में सक्षम नहीं होना चाहिए।

Btw,

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

बहुरूपी विलोपन के साथ एक आधार वर्ग के लिए।


7
# 2 के लिए +1, विशेष रूप से बहुरूपी विलोपन के बिना । यदि आपका विध्वंसक बेस पॉइंटर के माध्यम से कभी नहीं लगाया जा सकता है, तो यह आभासी अनावश्यक और अनावश्यक है, खासकर यदि आपकी कक्षा पहले आभासी नहीं थी (तो यह आरटीटीआई के साथ नए सिरे से बन जाती है)। इसका उल्लंघन करने वाले किसी भी उपयोगकर्ता के खिलाफ चौकस रहने के लिए, जैसा कि हर्ब सटर ने सलाह दी थी, आप बेस क्लास के डोर को संरक्षित और गैर-आभासी बना देंगे, ताकि यह केवल व्युत्पन्न विध्वंसक द्वारा / के बाद ही लागू किया जा सके।
अंडरस्कोर_ड

@underscore_d imho कि एक महत्वपूर्ण बिंदु जिसे मैंने जवाबों में याद किया, जैसा कि विरासत की उपस्थिति में एकमात्र मामला है जहां मुझे एक वर्चुअल कंस्ट्रक्टर की आवश्यकता नहीं है, जब मैं यह सुनिश्चित कर सकता हूं कि यह कभी भी आवश्यक नहीं है
पूर्व में जाने

14

आभासी विध्वंसक का उपयोग करने की लागत क्या है अगर मैं इसका उपयोग करता हूं, भले ही इसकी आवश्यकता न हो?

शुरू करने की लागत किसी भी एक वर्ग के लिए आभासी समारोह (विरासत में मिला है या वर्ग परिभाषा का हिस्सा) एक संभवतः बहुत खड़ी है (या नहीं वस्तु के आधार पर) तो की तरह, वस्तु प्रति संग्रहीत एक आभासी सूचक की प्रारंभिक लागत:

struct Integer
{
    virtual ~Integer() {}
    int value;
};

इस मामले में, मेमोरी की लागत अपेक्षाकृत भारी है। एक वर्ग उदाहरण का वास्तविक मेमोरी आकार अब अक्सर 64-बिट आर्किटेक्चर पर इस तरह दिखाई देगा:

struct Integer
{
    // 8 byte vptr overhead
    int value; // 4 bytes
    // typically 4 more bytes of padding for alignment of vptr
};

इस Integerवर्ग के लिए कुल 16 बाइट्स हैं, जो कि मात्र 4 बाइट्स के विपरीत है। अगर हम इनमें से एक लाख को एक सरणी में संग्रहीत करते हैं, तो हम 16 मेगाबाइट मेमोरी उपयोग के साथ समाप्त होते हैं: दो बार सामान्य 8 एमबी एल 3 सीपीयू कैश का आकार, और इस तरह के एक सरणी के माध्यम से पुनरावृत्ति करना 4 मेगाबाइट समकक्ष की तुलना में कई गुना धीमा हो सकता है। अतिरिक्त कैश मिस और पेज दोष के परिणामस्वरूप वर्चुअल पॉइंटर के बिना।

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

वर्चुअल पॉइंटर आमतौर पर ओवरहेड दृष्टिकोण से अधिक तत्काल चिंता का विषय है। हालांकि, एक आभासी सूचक के अलावा प्रति उदाहरण प्रति वर्ग लागत है। वर्चुअल फ़ंक्शंस के साथ प्रत्येक वर्ग एक vtableमेमोरी बनाता है जो उन फ़ंक्शन को संबोधित करता है जिन्हें वास्तव में कॉल करना चाहिए (वर्चुअल / डायनामिक डिस्पैच) जब वर्चुअल फ़ंक्शन कॉल किया जाता है। vptrउदाहरण के प्रति तो संग्रहित इस वर्ग विशेष को इंगित करता है vtable। यह ओवरहेड आमतौर पर कम चिंता का विषय है, लेकिन यह आपके द्विआधारी आकार को बढ़ा सकता है और थोड़ी सी रनटाइम लागत को जोड़ सकता है यदि इस ओवरहेड को एक जटिल कोडबेस में एक हजार वर्गों के लिए अनावश्यक रूप से भुगतान किया गया था, जैसे vtableकि लागत का यह पक्ष वास्तव में अधिक से अधिक के लिए आनुपातिक रूप से बढ़ता है। मिक्स में अधिक वर्चुअल फ़ंक्शंस।

प्रदर्शन-गंभीर क्षेत्रों में काम करने वाले जावा डेवलपर्स इस तरह के ओवरहेड को बहुत अच्छी तरह से समझते हैं (हालांकि अक्सर बॉक्सिंग के संदर्भ में वर्णित है), चूंकि एक जावा उपयोगकर्ता द्वारा परिभाषित प्रकार एक केंद्रीय objectआधार वर्ग से विरासत में मिला है और जावा में सभी फ़ंक्शन अंतर्निहित रूप से आभासी (ओवररिएडेबल) हैं ) प्रकृति में जब तक अन्यथा चिह्नित नहीं किया गया है। नतीजतन, एक जावा Integerइसी तरह vptr-स्टाइल मेटाडेटा प्रति उदाहरण से जुड़े के रूप में 64-बिट प्लेटफार्मों पर 16 बाइट्स मेमोरी की आवश्यकता होती है , और जावा intमें एक रनटाइम का भुगतान किए बिना किसी वर्ग में एकल की तरह लपेटना आमतौर पर असंभव है। इसके लिए प्रदर्शन लागत।

तो सवाल यह है: क्यों सी + + डिफ़ॉल्ट रूप से सभी विध्वंसक आभासी सेट नहीं करता है?

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

मुझे वर्चुअल विध्वंसक का उपयोग करने की आवश्यकता नहीं है?

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

class BaseClass
{
protected:
    // Disallow deleting/destroying subclass objects through `BaseClass*`.
    ~BaseClass() {}
};

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

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

std::vector, उदाहरण के लिए, विरासत में नहीं बनाया गया है और आम तौर पर विरासत में मिला नहीं होना चाहिए (बहुत ही अस्थिर डिजाइन), जैसा कि तब इस आधार सूचक विलोपन मुद्दे के लिए प्रवण हो जाएगा ( std::vectorजानबूझकर एक आभासी विध्वंसक से बचता है) क्लॉटी ऑब्जेक्ट स्लाइसिंग मुद्दों के अलावा यदि आपका व्युत्पन्न वर्ग किसी भी नए राज्य को जोड़ता है।

सामान्य रूप से विरासत में मिला एक वर्ग या तो एक सार्वजनिक आभासी विध्वंसक या एक संरक्षित, गैर-कानूनी होना चाहिए। से C++ Coding Standards, अध्याय ५०:

50. बेस क्लास डिस्ट्रक्टर्स को सार्वजनिक और आभासी, या संरक्षित और गैर-संवैधानिक बनाएं। हटाने के लिए, या हटाने के लिए नहीं; यह सवाल है: यदि एक बेस के लिए पॉइंटर के माध्यम से हटाने की अनुमति दी जानी चाहिए, तो बेस के विध्वंसक को सार्वजनिक और आभासी होना चाहिए। अन्यथा, यह संरक्षित और गैर-धार्मिक होना चाहिए।

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

उन मामलों में जहां आप बस मौजूदा कोड का पुन: उपयोग करना चाहते हैं, रचना को अक्सर प्रोत्साहित किया जाता है (समग्र पुन: उपयोग सिद्धांत)।


9

क्यों c ++ डिफ़ॉल्ट रूप से सभी विध्वंसक आभासी सेट नहीं करता है? वर्चुअल स्टोरेज टेबल के अतिरिक्त भंडारण और कॉल की लागत। C ++ सिस्टम के लिए उपयोग किया जाता है, कम-विलंबता, आरटी प्रोग्रामिंग जहां यह बोझ हो सकता है।


विध्वंसक वास्तविक समय प्रणालियों में पहले स्थान पर उपयोग नहीं किया जाना चाहिए क्योंकि गतिशील मेमोरी जैसे कई संसाधनों का उपयोग मजबूत समय-सीमा की गारंटी प्रदान करने के लिए नहीं किया जा सकता है
मार्को ए

9
@MarcoA। विनाशकारी कब से गतिशील मेमोरी आवंटन करते हैं?
chbaker0

@ chbaker0 मैंने एक 'जैसे' का इस्तेमाल किया। वे केवल मेरे अनुभव में उपयोग नहीं किए गए हैं।
मार्को ए

6
यह भी बकवास है कि गतिशील मेमोरी का उपयोग वास्तविक रीयल-टाइम सिस्टम में नहीं किया जा सकता है। यह साबित करने के लिए काफी तुच्छ है कि निश्चित आवंटन आकार और एक आवंटन बिटमैप के साथ एक पूर्वनिर्मित ढेर या तो मेमोरी आवंटित करेगा या उस बिटमैप को स्कैन करने में लगने वाले समय में एक आउट-ऑफ-मेमोरी स्थिति लौटाएगा।
MSalters

@msalters जो मुझे सोचते हैं: एक ऐसे कार्यक्रम की कल्पना करें जहां प्रत्येक ऑपरेशन की लागत को टाइप सिस्टम में संग्रहीत किया गया था। रीयलटाइम गारंटी के संकलन-समय की जाँच की अनुमति देता है।
यकायक

5

आभासी विध्वंसक का उपयोग नहीं करने के लिए यह एक अच्छा उदाहरण है: स्कॉट मेयर्स से:

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

// class for representing 2D points
class Point {
public:
    Point(short int xCoord, short int yCoord);
    ~Point();
private:
    short int x, y;
};

यदि एक छोटा इंट 16 बिट पर कब्जा कर लेता है, तो प्वाइंट ऑब्जेक्ट 32-बिट रजिस्टर में फिट हो सकता है। इसके अलावा, एक बिंदु वस्तु को अन्य भाषाओं जैसे सी या फोरट्रान में लिखे गए कार्यों के लिए 32-बिट मात्रा के रूप में पारित किया जा सकता है। यदि प्वाइंट के डिस्ट्रक्टर को आभासी बना दिया जाता है, हालांकि, स्थिति बदल जाती है।

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


If a class does not contain any virtual functions, that is often an indication that it is not meant to be used as a base class.Wut। क्या किसी और को अच्छे पुराने दिन याद हैं, जहां हमें पुन: प्रयोज्य सदस्यों और व्यवहार की क्रमिक परतों के निर्माण के लिए कक्षाओं और विरासत का उपयोग करने की अनुमति दी गई थी, बिना आभासी तरीकों की परवाह किए बिना? C'mon, स्कॉट। मुझे मूल बिंदु मिलता है, लेकिन यह "अक्सर" वास्तव में पहुंच रहा है।
अंडरस्कोर_ड

3

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

  • यदि व्युत्पन्न वर्गों के उदाहरण ढेर पर आवंटित नहीं किए जाते हैं, उदाहरण के लिए केवल सीधे स्टैक पर या अन्य वस्तुओं के अंदर। (यदि आप एकतरफा मेमोरी और प्लेसमेंट ऑपरेटर नया उपयोग करते हैं तो छोड़कर।)
  • यदि व्युत्पन्न वर्गों के उदाहरण ढेर पर आवंटित किए जाते हैं, लेकिन विलोपन केवल पॉइंटर्स के माध्यम से सबसे व्युत्पन्न वर्ग के लिए होता है, जैसे कि ए है std::unique_ptr<Derived>, और बहुरूपता केवल गैर-मालिक बिंदुओं और संदर्भों के माध्यम से होता है। एक और उदाहरण है जब वस्तुओं का उपयोग करके आवंटित किया जाता है std::make_shared<Derived>()std::shared_ptr<Base>जब तक शुरुआती पॉइंटर a था तब तक उपयोग करना ठीक है std::shared_ptr<Derived>। इसका कारण यह है कि साझा किए गए पॉइंटर्स के पास विध्वंसक (डेलेटर) के लिए अपना स्वयं का गतिशील प्रेषण होता है जो जरूरी नहीं कि आभासी बेस क्लास डिस्ट्रक्टर पर निर्भर हो।

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

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


1

एक विध्वंसक की घोषणा virtualकेवल तभी आवश्यक है जब आप अपनी classविरासत बनाने की योजना बनाते हैं । आमतौर पर मानक पुस्तकालय (जैसे std::string) की कक्षाएं एक आभासी विध्वंसक प्रदान नहीं करती हैं और इस प्रकार उपवर्ग के लिए नहीं होती हैं।


3
कारण बहुरूपता + बहुरूपता का उपयोग है। एक आभासी विध्वंसक की आवश्यकता तभी होती है जब एक गतिशील संकल्प की आवश्यकता होती है, जो कि एक संदर्भ / सूचक है / जो भी मास्टर वर्ग वास्तव में एक उपवर्ग की आवृत्ति का उल्लेख कर सकता है।
मिशेल बिल्लौद

2
@MichelBillaud वास्तव में आप अभी भी वर्चुअल डार्टर के बिना बहुरूपता हो सकते हैं। पॉलीमॉर्फिक डिलीट के लिए एक वर्चुअल डोर की आवश्यकता होती है, अर्थात deleteएक पॉइंटर को एक पॉइंटर पर कॉल करना।
chbaker0

1

कंस्ट्रक्टर में ओवरहेड बनाने के लिए एक ओवरहेड होगा (यदि आपके पास अन्य वर्चुअल फ़ंक्शन नहीं हैं, तो जिस स्थिति में आप PROBABLY हैं, लेकिन हमेशा नहीं, एक वर्चुअल विध्वंसक भी होना चाहिए)। और यदि आपके पास कोई अन्य आभासी कार्य नहीं है, तो यह आपकी वस्तु को एक सूचक-आकार से बड़ा बनाता है अन्यथा आवश्यक नहीं है। जाहिर है, बढ़े हुए आकार का छोटी वस्तुओं पर बड़ा प्रभाव हो सकता है।

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

आपके पास एक वर्चुअल विध्वंसक होना चाहिए यदि आपकी कक्षा को बेस-क्लास के रूप में अभिप्रेत है, विशेष रूप से यदि यह कोड के अलावा किसी अन्य इकाई द्वारा बनाया / नष्ट किया जा सकता है जो जानता है कि यह किस प्रकार का है, तो आपको एक वर्चुअल विध्वंसक की आवश्यकता है।

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

संक्षेप में, आपके पास एक वर्चुअल विध्वंसक नहीं होना चाहिए यदि: 1. आपके पास कोई वर्चुअल फ़ंक्शन नहीं है। 2. कक्षा से प्राप्त न करें (इसे finalC ++ 11 में चिह्नित करें , इस तरह संकलक बताएगा कि क्या आप इसे प्राप्त करने का प्रयास करते हैं)।

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

संक्षेप में, एक छोटा ओवरहेड है, लेकिन छोटी वस्तुओं के लिए, इसमें अंतर हो सकता है।

ध्यान दें कि कंपाइलर कुछ मामलों में वर्चुअल लुकअप को दूर कर सकते हैं, इसलिए यह केवल एक दंड है

हमेशा की तरह जब यह प्रदर्शन, मेमोरी फ़ुटप्रिंट, और जैसे: बेंचमार्क और प्रोफ़ाइल और माप, विकल्पों के साथ परिणामों की तुलना करता है, और यह देखें कि समय / मेमोरी का MOST कहाँ खर्च होता है, और 90% का अनुकूलन करने की कोशिश न करें कोड जो बहुत अधिक नहीं चलाया जाता है [अधिकांश अनुप्रयोगों में लगभग 10% कोड होता है जो निष्पादन के समय पर अत्यधिक प्रभावशाली होता है, और 90% कोड जिसमें बहुत अधिक प्रभाव नहीं होता है]। एक उच्च अनुकूलन स्तर में ऐसा करें, इसलिए आपको पहले से ही एक अच्छा काम करने वाले कंपाइलर का लाभ होगा! और दोहराएं, फिर से जांच करें और स्टेप वाइज सुधार करें। चालाक बनने की कोशिश मत करो और यह पता लगाने की कोशिश करो कि क्या महत्वपूर्ण है और क्या नहीं है जब तक कि आपके पास उस विशेष प्रकार के आवेदन के साथ बहुत अनुभव न हो।


1
कंपाइलर द्वारा प्रति-वर्ग के आधार पर "वाइबल की रचना " - वेटेबल बनाने के लिए "कंस्ट्रक्टर में एक ओवरहेड होगा" , कंस्ट्रक्टर के पास केवल निर्माण के तहत ऑब्जेक्ट में एक पॉइंटर को स्टोर करने का ओवरहेड होने के साथ।
टोनी

इसके अलावा ... मैं समय से पहले अनुकूलन से बचने के बारे में हूँ, लेकिन इसके विपरीत, You **should** have a virtual destructor if your class is intended as a base-classएक स्थूल निरीक्षण है - और समय से पहले निराशा । यह केवल तभी आवश्यक है जब किसी को सूचक से आधार तक एक व्युत्पन्न वर्ग को हटाने की अनुमति दी जाए। कई स्थितियों में, ऐसा नहीं है। यदि आप जानते हैं कि यह है, तो सुनिश्चित करें, ओवरहेड को उकसाएं। जो, btw, हमेशा जोड़ा जाता है, भले ही वास्तविक कॉल संकलक द्वारा सांख्यिकीय रूप से हल किया जा सकता है। अन्यथा, जब आप ठीक से नियंत्रित करते हैं कि लोग आपकी वस्तुओं के साथ क्या कर सकते हैं, यह सार्थक नहीं है
अंडरस्कोर_ड
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.