هل يتم استدعاء Destructor من فئة C ++ التي ترمي استثناء؟

StackOverflow https://stackoverflow.com/questions/3129359

سؤال

لنفترض أن لدي فئة مثل هذا:

#include <iostream>

using namespace std;

class Boda {
    private:
        char *ptr;

    public:
        Boda() {
            ptr = new char [20];
        }
        ~Boda() {
            cout << "calling ~Boda\n";

            delete [] ptr;
        }

        void ouch() {
            throw 99;
        }
};

void bad() {
    Boda b;
    b.ouch();
}

int main() {
    bad();
}

يبدو ذلك المدمر ~Boda لم يتم استدعاؤه أبدًا ، وبالتالي ptr المورد لا يتم إطلاق سراحه.

هنا إخراج البرنامج:

terminate called after throwing an instance of 'int'
Aborted

لذلك يبدو أن إجابة سؤالي No.

لكنني اعتقدت أن المكدس أصبح غير ملزم عندما يتم إلقاء استثناء؟ لماذا لم تفعل Boda b يتم تدمير الكائن في مثالي؟

الرجاء مساعدتي في فهم مشكلة المورد هذه. أريد أن أكتب برامج أفضل في المستقبل.

أيضا ، هل هذا هو ما يسمى RAII?

شكرا ، بودا cydo.

هل كانت مفيدة؟

المحلول

إذا لم يتم اكتشاف الاستثناء في أي مكان ، فإن وقت تشغيل C ++ مجاني للذهاب مباشرة إلى إنهاء البرنامج دون القيام بأي مكدس للاسترخاء أو استدعاء أي تدمير.

ومع ذلك ، إذا قمت بإضافة كتلة تجريب حول المكالمة إلى bad(), ، سترى المدمرة ل Boda الكائن يسمى:

int main() {
    try {
      bad();
    } catch(...) {  // Catch any exception, forcing stack unwinding always
      return -1;
    }
}

راي تعني أن الذاكرة المخصصة ديناميكيًا (كومة) مملوكة دائمًا بواسطة كائن مخصص تلقائيًا (المكدس) الذي يعامله عند تدمير الكائن. يعتمد هذا على ضمان أن يتم استدعاء المدمر عند خروج الكائن المتمثل تلقائيًا عن النطاق ، سواء بسبب عودة طبيعية أو بسبب استثناء.

عادةً ما لا يمثل سلوك الحالة الزاوية مشكلة فيما يتعلق بـ RAII ، حيث عادة ما يكون السبب الرئيسي وراء رغبتك في تشغيل المدمرين هو تحرير الذاكرة ، ويتم إعادة جميع الذاكرة إلى نظام التشغيل عندما ينتهي البرنامج على أي حال. ومع ذلك ، إذا قام المدمرون الخاص بك بشيء أكثر تعقيدًا ، مثل إزالة ملف قفل على القرص أو شيء من هذا القبيل ، حيث سيحدث فرقًا سواء كان البرنامج يسمى Destructors أم لا عند التعطل ، قد ترغب في لف main في كتلة التجربة التي تمسك بكل شيء (فقط للخروج من الاستثناء على أي حال) فقط للتأكد من أن المكدس يستريح دائمًا قبل الانتهاء.

نصائح أخرى

لن يتم تشغيل المدمر في حالة حدوث استثناء في المنشئ.

سيتم تشغيله إذا لزم الأمر (إذا تم التعامل مع الاستثناء في مكان ما) إذا تم رفع الاستثناء بطريقة أخرى كما في مثالك. ولكن مع إنهاء البرنامج ، فإن استدعاء المدمر ليس ضروريًا هنا ويعتمد السلوك على المترجم ...

فكرة RAII هل هذا المنشئ يخصص ريسورس ويحررهم المدمر. في حالة حدوث استثناء في مُنشئ ، لا توجد طريقة بسيطة لمعرفة عمليات التخصيص التي تم تخصيصها والتي لم تكن (يعتمد على المكان المحدد في المُنشئ الذي حدث فيه استثناء). يجب أن تتذكر أيضًا أنه في حالة فشل مُنشئ ، فإن الطريقة الوحيدة لقول ذلك لاستثناء الاستثناء و يتم تحرير الذاكرة المخصصة (إما استرخاء المكدس ، أو الذاكرة المخصصة للكومة) كما لو لم يتم تخصيصها أبدًا.

الحل واضح: إذا كان قد يحدث أي استثناء داخل مُنشئ ، فيجب عليك التقاطه وحرصات المخصصات المخصصة إذا لزم الأمر. قد يكون هذا في الواقع بعض الكود المكررة مع Destructor ، ولكن هذه ليست مشكلة كبيرة.

في Destructor يجب ألا تثير استثناءات ، لأنه يمكن أن يؤدي إلى مشاكل كبيرة مع استرخاء المكدس.

في أي طريقة أخرى ، استخدم الاستثناءات كما تريد ، ولكن لا تنسى التعامل معها في مكان ما. قد يكون الإدراك غير المعقول أسوأ من عدم الاستثناء على الإطلاق. أعرف بعض البرامج التي لا تتعامل مع استثناءات لبعض الأخطاء البسيطة ... وتحطم الأخطاء التي يجب أن تصدر تحذيرًا فقط.

حاول أن تتدفق الدفق - سترى أن المدمر يسمى بالفعل:

cout << "calling ~Boda" << endl;

إنه التخزين المؤقت للإدخال/الإخراج هو الذي يؤخر النسخة المطبوعة إلى النقطة التي يخفضها إنهاء البرنامج قبل الإخراج الفعلي.

يحرر:

ما سبق يحمل استثناءات معالجة. مع استثناءات غير معطلة لا يحدد المعيار ما إذا كان المكدس غير ملزم أم لا. أنظر أيضا هذا السؤال جدا.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top