অপারেটরটিতে ডাস্ট্রাক্টরকে ডিলিট করা হয় না কেন?


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

প্রত্যাশিত:

ধ্বংসকারীকে দুটি কল রয়েছে calls ওভারলোড কল করার আগে এক 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ধরণের বস্তুর মুছে ফেলার
এমএম

@ এমএমএম প্রধান সংকলকরা এতে সর্বাধিক সতর্ক করে দেয়, তাই আমি বুঝতে পারি নি যে void*অপারেন্ড এমনকি স্পষ্টতই খারাপভাবে তৈরি। [expr.delete] / 1 : " অপারেন্ডটি অবজেক্ট টাইপ বা ক্লাস টাইপের পয়েন্টার হতে পারে [[...] এর দ্বারা বোঝা যায় যে কোনও বস্তু টাইপ শূন্যের পয়েন্টার ব্যবহার করে মুছে ফেলা যায় না কারণ শূন্যস্থান কোনও বস্তুর প্রকার নয়। * "@OP আমি আমার উত্তরটি সংশোধন করেছি।
আখরোট

উত্তর:


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();, যা এরপরে এই স্মৃতিতে অন্য একটি অবজেক্ট তৈরি করবে , এর ডিস্ট্রাক্টরকে ফোন না করে পূর্ববর্তী অবজেক্টের জীবনকাল শেষ করবে।


সম্পাদনা:

প্রশ্নে @ এমএম এর মন্তব্যে ধন্যবাদ, আমি বুঝতে পেরেছি যে একটি void*অপারেন্ড ::deleteআসলে দুর্গঠিত । ( [expr.delete] / 1 ) তবে, প্রধান সংকলকরা কেবল ত্রুটি নয়, কেবল এ সম্পর্কে সতর্ক করার সিদ্ধান্ত নিয়েছে বলে মনে হয়। এটি ইতিমধ্যে সংজ্ঞায়িত আচরণ ব্যবহার করে ::deleteএটি অশুভ-গঠনের আগে void*, এই প্রশ্নটি দেখুন

অতএব, আপনার প্রোগ্রামটি দুর্গঠিত এবং আপনার যদি কোনও গ্যারান্টি নেই যে কোডটি এখনও উপরে সংকলিত করতে সক্ষম হয়েছে যদি আমি উপরে বর্ণিত তেমন আচরণ করে।


@ স্যান্ডারডাইকার তার উত্তরের নীচে যেমন উল্লেখ করেছেন, আপনারও অপরিজ্ঞাত আচরণ রয়েছে কারণ মেমরিতে অন্য একটি MyClassঅবজেক্ট তৈরি করে যা ইতিমধ্যে কোনও অবজেক্টের ডেস্ট্রাক্টরকে ফোন না করে আপনি [বেসিক.লাইফ] / 5 লঙ্ঘন করছেন যা যদি এটি করা নিষেধ করে তবে প্রোগ্রামটি ডেস্ট্রাক্টরের পার্শ্ব প্রতিক্রিয়াগুলির উপর নির্ভর করে। এই ক্ষেত্রে printfডেস্ট্রাক্টরের বিবৃতিতে এরকম পার্শ্ব প্রতিক্রিয়া রয়েছে।


অপব্যবহারের উদ্দেশ্য এই অপারেটরগুলি কীভাবে কাজ করে তা যাচাই করা। তবে, আপনার উত্তরের জন্য ধন্যবাদ। মনে হচ্ছে এটিই একমাত্র উত্তর যা আমার সমস্যা সমাধান করে।

13

আপনার শ্রেণি-নির্দিষ্ট ওভারলোডগুলি ভুলভাবে সম্পন্ন হয়েছে। এটি আপনার আউটপুটে দেখা যাবে: কনস্ট্রাক্টরকে দুবার ডাকা হয়!

শ্রেণি-নির্দিষ্টে operator new, গ্লোবাল অপারেটরকে সরাসরি কল করুন:

return ::operator new(size);

একইভাবে, ক্লাস-নির্দিষ্টে operator delete, করুন:

::operator delete(p);

পড়ুন operator newআরো বিস্তারিত জানার জন্য রেফারেন্স পাতা।


আমি জানি কন্সট্রাক্টরকে দুবার ডেকে ডাকা হয় :: নতুন অপারেটরে নতুন এবং আমি এটি বোঝাতে চাইছি। আমার প্রশ্ন হ'ল অপারেটর মুছতে মুছতে :: কল করার সময় কেন ডেস্ট্রাক্টর বলা হয় না?

1
@expinc: ইচ্ছাকৃতভাবে কলিং বিনাশকারী ছাড়া কন্সট্রাকটর আহ্বান একটি দ্বিতীয় সময় প্রথম একটি সত্যিই খারাপ ধারণা। তুচ্ছ ত্রুটিযুক্ত বিনাশক (যেমন আপনার), আপনি এমনকি অপরিজ্ঞাত আচরণের অঞ্চলে প্রবেশ করছেন (যদি আপনি ধ্বংসকারীর পার্শ্ব প্রতিক্রিয়াগুলির উপর নির্ভর করেন তবে আপনি যা করেন) - রেফ। [বেসিক.লাইফ] §5 । এটি করবেন না।
Sander De Dycker

1

সিপিপি রেফারেন্স দেখুন :

operator delete, operator delete[]

পূর্বে কোনও মিলের দ্বারা বরাদ্দকৃত স্টোরেজকে ডিলোকেট করে operator new। এই ডিএলোকেশন ফাংশনগুলিকে ডিলিফিক এক্সপ্রেশন এবং ডায়ামিক স্টোরেজ সময়কালের সাথে অবজেক্টগুলি ধ্বংস করার পরে (বা নির্মাণে ব্যর্থ হওয়া) মেমরিটিকে কমিয়ে আনতে নতুন এক্সপ্রেশন দ্বারা ডাকা হয়। এগুলিকে নিয়মিত ফাংশন কল সিনট্যাক্স ব্যবহার করে ডাকা যেতে পারে।

মুছুন (এবং নতুন) কেবলমাত্র 'মেমরি পরিচালনা' অংশের জন্য দায়বদ্ধ।

সুতরাং এটি স্পষ্ট এবং প্রত্যাশিত যে ধ্বংসকারীকে কেবল একবার ডাকা হয় - অবজেক্টের উদাহরণটি পরিষ্কার করতে। এটি কি দুইবার কল করা হবে, প্রতিটি ধ্বংসকারীকে এটি ইতিমধ্যে কল করা হয়েছে কিনা তা পরীক্ষা করে দেখতে হবে।


1
মুছে ফেলা অপারেটরটিকে তাত্পর্যপূর্ণভাবে ডেস্ট্রাক্টরকে কল করা উচিত, আপনি উদাহরণটি মোছার পরে ধ্বংসকারীকে দেখানো তার নিজের লগগুলি থেকে দেখতে পাবেন। এখানে সমস্যাটি হ'ল তার ক্লাস ডিলিট ওভাররাইড কল করছে: ডিলিট যা তার বিশ্বব্যাপী মোছার ওভাররাইডের দিকে নিয়ে যায়। এই গ্লোবাল ডিলিট ওভাররাইড কেবল মেমরিকে মুক্তি দেয় যাতে এটি ডেস্ট্রাক্টরকে ফিরিয়ে দেয় না।
পিকল রিক

রেফারেন্সটি পরিষ্কারভাবে জানিয়েছে যে মুছুনটিকে আফটার অবজেক্ট ডিকানস্ট্রাকশন বলা হয়
মারিও দ্য চামচ

হ্যাঁ, ক্লাস মুছার পরে গ্লোবাল ডিলিট বলা হয়। এখানে দুটি ওভাররাইড রয়েছে।
পিকল রিক

2
@ পিকরিক - এটি সত্য যে মুছে ফেলা অভিব্যক্তিটি একজন ডেস্ট্রাক্টরকে কল করা উচিত (ধরে নিলেন যে কোনও ধরণের ডিস্ট্রাক্টরের সাথে একটি পয়েন্টার সরবরাহ করা হয়েছে) বা ডেস্ট্রাক্টরের একটি সেট (অ্যারে ফর্ম), কোনও operator delete()ফাংশন মুছে ফেলার মত প্রকাশের মত নয়। operator delete()ফাংশন বলার আগে ডেস্ট্রাক্টরকে ডাকা হয়।
পিটার

1
আপনি কউটে কোনও শিরোনাম যুক্ত করলে এটি সহায়তা করবে। বর্তমানে এটি কী বোঝায় তা পরিষ্কার নয়। "স্টোরেজকে ডেলোকেট করে ..." - স্টোরেজকে কে হ্রাস করে?
idclev 463035818
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.