विध्वंसक को ऑपरेटर डिलीट में क्यों नहीं कहा जाता है?


16

मैंने इसमें ::deleteएक वर्ग के लिए कॉल करने की कोशिश की operator delete। लेकिन विध्वंसक नहीं कहा जाता है।

मैंने एक ऐसे वर्ग को परिभाषित किया MyClassजिसका operator deleteभार अधिक हो गया है। वैश्विक operator deleteभी अतिभारित है। अतिभारित operator deleteकी MyClassअतिभारित वैश्विक फोन करेगा operator delete

class MyClass
{
public:
    MyClass() { printf("Constructing MyClass...\n"); }
    virtual ~MyClass() { printf("Destroying MyClass...\n"); }

    void* operator new(size_t size)
    {
        printf("Newing MyClass...\n");
        void* p = ::new MyClass();
        printf("End of newing MyClass...\n");
        return p;
    }

    void operator delete(void* p)
    {
        printf("Deleting MyClass...\n");
        ::delete p;    // Why is the destructor not called here?
        printf("End of deleting MyClass...\n");
    }
};

void* operator new(size_t size)
{
    printf("Global newing...\n");
    return malloc(size);
}

void operator delete(void* p)
{
    printf("Global deleting...\n");
    free(p);
}

int main(int argc, char** argv)
{
    MyClass* myClass = new MyClass();
    delete myClass;

    return EXIT_SUCCESS;
}

आउटपुट है:

Newing MyClass...
Global newing...
Constructing MyClass...
End of newing MyClass...
Constructing MyClass...
Destroying MyClass...
Deleting MyClass...
Global deleting...
End of deleting MyClass...

वास्तविक:

ओवरलोड होने से पहले विध्वंसक को केवल एक कॉल करना operator deleteहै MyClass

अपेक्षित होना:

विध्वंसक को दो कॉल हैं। अतिभारित कॉल करने से पहले एक operator deleteके MyClass। ग्लोबल कॉल करने से पहले एक और operator delete


6
MyClass::operator new()(कम से कम) sizeबाइट्स में कच्ची मेमोरी आवंटित करनी चाहिए । यह पूरी तरह से एक उदाहरण का निर्माण करने का प्रयास नहीं करना चाहिए MyClass। के निर्माण के MyClassबाद निष्पादित किया जाता है MyClass::operator new()। फिर, डिस्ट्रक्टर deleteको main()कॉल करता है, और मेमोरी को रिलीज़ करता है (डिस्ट्रॉक्टर को फिर से कॉल किए बिना)। ::delete pअभिव्यक्ति वस्तु के प्रकार के बारे में कोई जानकारी नहीं है pके बाद से, पर अंक pएक है void *, इसलिए नाशक आह्वान नहीं कर सकते।
पीटर


2
आपको पहले से दी गई प्रतिक्रियाएँ सही हैं, लेकिन मुझे आश्चर्य है: आप नए को ओवरराइड करने और हटाने की कोशिश क्यों कर रहे हैं? विशिष्ट उपयोग का मामला कस्टम मेमोरी मैनेजमेंट (जीसी, मेमोरी जो कि डिफ़ॉल्ट मॉलोक (), आदि से नहीं आता है) को लागू कर रहा है। हो सकता है कि आप जो हासिल करने की कोशिश कर रहे हैं, उसके लिए आप गलत टूल का इस्तेमाल कर रहे हों।
noamtm

2
::delete p;अपरिभाषित व्यवहार का कारण बनता है क्योंकि जिस प्रकार का *pऑब्जेक्ट हटाया जा रहा है उसी प्रकार का नहीं है (और न ही वर्चुअल डिस्ट्रक्टर के साथ बेस क्लास)
MM

@MM प्रमुख संकलक केवल इस पर चेतावनी देते हैं, इसलिए मुझे यह महसूस नहीं हुआ कि void*जैसा कि ऑपरेंड भी स्पष्ट रूप से बीमार है। [expr.delete] / 1 : " ऑपरेंड ऑब्जेक्ट प्रकार या वर्ग प्रकार के लिए पॉइंटर का होगा। [...] इसका मतलब यह है कि ऑब्जेक्ट को किसी प्रकार के शून्य के पॉइंटर का उपयोग करके हटाया नहीं जा सकता क्योंकि शून्य कोई ऑब्जेक्ट प्रकार नहीं है। * @ @ मैंने अपने उत्तर में संशोधन किया है।
अखरोट

जवाबों:


17

आप का दुरुपयोग कर रहे हैं operator newऔर operator delete। ये संचालक आवंटन और निपटान कार्य हैं। वे वस्तुओं के निर्माण या विनाश के लिए जिम्मेदार नहीं हैं। वे केवल उस मेमोरी को प्रदान करने के लिए जिम्मेदार हैं जिसमें ऑब्जेक्ट रखा जाएगा।

इन कार्यों के वैश्विक संस्करण हैं ::operator newऔर ::operator delete::newऔर ::deleteनए / डिलीट-एक्सप्रेशन हैं, जैसे हैं , new/ delete, उन लोगों से भिन्न हैं, उस में ::newऔर ::deleteक्लास-विशिष्ट operator new/ operator deleteओवरलोड को बायपास करेगा ।

नए / डिलीट-एक्सप्रेशंस कंस्ट्रक्शन / डिस्ट्रक्ट और एलोकेशन / डीललॉकेट करते हैं (उपयुक्त कॉल करके operator newया operator deleteकंस्ट्रक्शन से पहले या विनाश के बाद)।

चूंकि आपका अधिभार आवंटन / आवंटन रद्द भाग के लिए जिम्मेदार है, यह फोन करना चाहिए ::operator newऔर ::operator deleteके बजाय ::newऔर ::delete

deleteमें delete myClass;नाशक फोन करने के लिए जिम्मेदार है।

::delete p;विध्वंसक को कॉल नहीं करता है क्योंकि pटाइप है void*और इसलिए अभिव्यक्ति को पता नहीं चल सकता है कि विध्वंसक को क्या कॉल करना है। यह संभवतः ::operator deleteमेमोरी void*को हटाने के लिए आपके बदले हुए स्थान को कॉल करेगा , हालाँकि डिलीट-एक्सप्रेशन के रूप में ऑपरेंड के रूप में उपयोग करना बीमार है (नीचे संपादित करें देखें)।

::new MyClass();::operator newस्मृति को आवंटित करने के लिए आपके द्वारा प्रतिस्थापित कॉल और इसमें एक ऑब्जेक्ट का निर्माण करता है। इस ऑब्जेक्ट के लिए पॉइंटर को void*नए-एक्सप्रेशन के रूप में लौटाया जाता है MyClass* myClass = new MyClass();, जो बाद में इस मेमोरी में एक अन्य ऑब्जेक्ट का निर्माण करेगा , पिछली ऑब्जेक्ट के जीवनकाल को उसके विध्वंसक को बुलाए बिना समाप्त कर देगा।


संपादित करें:

इस सवाल पर @ MM की टिप्पणी के कारण, मुझे एहसास हुआ कि एक void*ऑपरेंड ::deleteवास्तव में बीमार है। ( [expr.delete] / 1 ) हालांकि, प्रमुख कंपाइलरों ने केवल इस बारे में चेतावनी देने का फैसला किया है, त्रुटि नहीं। इससे पहले कि वह बीमार बना था, पहले से ही अपरिभाषित व्यवहार ::deleteपर void*था, इस प्रश्न को देखें ।

इसलिए, आपका कार्यक्रम बीमार बना हुआ है और आपके पास कोई गारंटी नहीं है कि कोड वास्तव में वही करता है जो मैंने ऊपर वर्णित किया था अगर यह अभी भी संकलन करने में कामयाब रहा।


जैसा कि उनके जवाब के नीचे @SanderDeDycker द्वारा बताया गया है, आपके पास अपरिभाषित व्यवहार भी है क्योंकि किसी अन्य ऑब्जेक्ट को मेमोरी में पहले से ही MyClassउस ऑब्जेक्ट के डिस्ट्रक्टर को कॉल किए बिना निर्माण करके आप पहले [बेसिक.लाइफ] का उल्लंघन कर रहे हैं [5] जो ऐसा करने से मना करता है प्रोग्राम विध्वंसक के दुष्प्रभावों पर निर्भर करता है। इस मामले में printfविध्वंसक के बयान का ऐसा दुष्प्रभाव है।


दुरुपयोग की जाँच करने का इरादा है कि ये ऑपरेटर कैसे काम करता है। हालाँकि, आपके उत्तर के लिए धन्यवाद। यह एकमात्र उत्तर लगता है जो मेरी समस्या को हल करता है।
निष्कासित

13

आपके वर्ग-विशिष्ट ओवरलोड गलत तरीके से किए गए हैं। यह आपके आउटपुट में देखा जा सकता है: निर्माणकर्ता को दो बार कहा जाता है!

कक्षा-विशिष्ट में operator new, सीधे वैश्विक ऑपरेटर को कॉल करें:

return ::operator new(size);

इसी प्रकार, क्लास-विशिष्ट में operator delete, करें:

::operator delete(p);

का संदर्भ लें operator newअधिक जानकारी के लिए संदर्भ पृष्ठ।


मुझे पता है कि कंस्ट्रक्टर को कॉल करके दो बार बुलाया जाता है :: ऑपरेटर नया में नया और मेरा मतलब है। मेरा प्रश्न यह है कि कॉल करते समय विध्वंसक को क्यों नहीं बुलाया जाता है :: ऑपरेटर को हटाएं?
निष्कासित करें

1
@expinc: जानबूझकर बुला नाशक बिना निर्माता बुला दूसरी बार पहले एक बहुत बुरा विचार है। गैर-तुच्छ विध्वंसक (आपकी तरह) के लिए, आप अपरिभाषित व्यवहार क्षेत्र में भी उद्यम कर रहे हैं (यदि आप विध्वंसक के दुष्प्रभावों पर निर्भर हैं, जो आप करते हैं) - रेफ। [basic.life] §5 । यह मत करो।
Sander De Dycker

1

सीपीपी संदर्भ देखें :

operator delete, operator delete[]

पहले से मेल खाते द्वारा आवंटित किए गए संग्रहण को हटाता है operator new। डायलेक्टेशन फ़ंक्शंस को डिलीट-एक्सप्रेशन और नए-एक्सप्रेशन द्वारा डायनामिक स्टोरेज अवधि के साथ ऑब्जेक्ट्स को नष्ट (या निर्माण करने में विफल) के बाद मेमोरी डील करने के लिए कहा जाता है। उन्हें नियमित फ़ंक्शन कॉल सिंटैक्स का उपयोग करके भी बुलाया जा सकता है।

हटाएं (और नया) केवल 'मेमोरी प्रबंधन' भाग के लिए जिम्मेदार हैं।

तो यह स्पष्ट और अपेक्षित है कि विध्वंसक को केवल एक बार कहा जाता है - वस्तु के उदाहरण को साफ करने के लिए। क्या इसे दो बार कहा जाएगा, हर विध्वंसक को यह जांचना होगा कि क्या यह पहले से ही बुलाया गया था।


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

संदर्भ में स्पष्ट रूप से कहा गया है कि डिलीट को AFTER ऑब्जेक्ट डिकंस्ट्रक्शन कहा जाता है
मारियो द स्पून

हाँ, ग्लोबल डिलीट को क्लास डिलीट के बाद कहा जाता है। यहां दो ओवरराइड हैं।
अचार रिक रिक

2
@PickleRick - जबकि यह सच है कि एक डिलीट एक्सप्रेशन को एक डिस्ट्रक्टर (एक डिस्ट्रक्टर के साथ एक प्रकार को पॉइंटर सप्लाई करने वाला) या डिस्ट्रक्टर्स (एरे फॉर्म) का एक सेट कहा जाना चाहिए, एक operator delete()फंक्शन एक डिलीट एक्सप्रेशन जैसी चीज नहीं है। विध्वंसक को operator delete()फ़ंक्शन कहा जाता है से पहले कहा जाता है।
पीटर

1
यदि आपने एक हेडर को qoute में जोड़ा तो यह मदद करेगा। वर्तमान में यह स्पष्ट नहीं है कि यह किसको संदर्भित करता है। "डेलोकेट्स स्टोरेज ..." - स्टोरेज को कौन डील करता है?
idclev 463035818
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.