سؤال

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

void f() {
    SomeKindOfException ex(...);
    throw ex;
}

void g() {
    try {
        f();
    } catch (SomeKindOfException& ex) {
        //Handling code...
    }
}

لقد أضفت عبارة طباعة إلى SomekindofException's Destructor وتظهر أن السابقين يدمج بمجرد خروجه من النطاق في F () ولكن بعد ذلك اشتعلت في G () وتدمير مرة أخرى بمجرد الخروج من النطاق هناك أيضا.

أي مساعدة؟

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

المحلول

يتم نسخ كائن الاستثناء إلى موقع خاص للبقاء على قيد الحياة من المكدس المرافق. السبب في رؤية اثنين من التدمير هو لأنه عند الخروج من F () يتم تدمير الاستثناء الأصلي وعند الخروج من G () يتم تدمير النسخة.

نصائح أخرى

يتم نسخ الكائن إلى كائن استثناء التي تنج على البقاء على قيد الحياة مكدس. حيث يأتي الذاكرة لهذا الكائن غير محدد. لكائن كبير، ربما سيكون malloc"ED، وكائنات أصغر، يمكن أن يكون التنفيذ مؤقتا مخصصا مسبقا (يمكنني أن أتخيل أنه يمكن استخدام ذلك ل bad_alloc استثناء).

المرجع ex ثم ملتزم بذلك كائن استثناء, ، وهو مؤقت (ليس له اسم).

C ++ Standard 15.1 / 4:

يتم تخصيص الذاكرة للحصول على نسخة مؤقتة من الاستثناء الذي يتم إلقاؤه بطريقة غير محددة، إلا كما لوحظ في 3.7.3.1. استمر المؤقت طالما كان هناك معالج يتم تنفيذه لهذا الاستثناء. على وجه الخصوص، إذا مختطف المعالج عن طريق تنفيذ رمي؛ بيان، الذي يمر التحكم في معالج آخر لنفس الاستثناء، وبالتالي فإن المؤقتة. عندما يتم تنفيذه المعالج الأخير للاستثناءات بأي وسيلة غير رمي؛ يتم تدمير الكائن المؤقت ويجوز التنفيذ إلغاء الذاكرة للكائن المؤقت؛ يتم أي وكيل من هذا القبيل بطريقة غير محددة. يحدث التدمير مباشرة بعد تدمير الكائن المعلن في إعلان الاستثناء في المعالج.

لا يوجد شيء آخر يمكن أن أقوله.

عندما ترمي السابقين، يتم نسخه في موقع ذاكرة خاص يستخدم لكائنات استثناء ألقيت. يتم تنفيذ هذه النسخة من قبل منشئ النسخ الطبيعي.

يمكنك أن ترى هذا بسهولة من هذا المثال:

#include <iostream>

void ThrowIt();

class TestException
{
  public:
    TestException()
    {
        std::cerr<<this<<" - inside default constructor"<<std::endl;
    }

    TestException(const TestException & Right)
    {
        (void)Right;
        std::cerr<<this<<" - inside copy constructor"<<std::endl;
    }

    ~TestException()
    {
        std::cerr<<this<<" - inside destructor"<<std::endl;    
    }
};

int main()
{
    try
    {
        ThrowIt();
    }
    catch(TestException & ex)
    {
        std::cout<<"Caught exception ("<<&ex<<")"<<std::endl;
    }
    return 0;
}

void ThrowIt()
{
    TestException ex;
    throw ex;
}

الناتج العينة:

matteo@teolapubuntu:~/cpp/test$ g++ -O3 -Wall -Wextra -ansi -pedantic ExceptionStack.cpp -o ExceptionStack.x
matteo@teolapubuntu:~/cpp/test$ ./ExceptionStack.x 
0xbf8e202f - inside default constructor
0x9ec0068 - inside copy constructor
0xbf8e202f - inside destructor
Caught exception (0x9ec0068)
0x9ec0068 - inside destructor

بالمناسبة، يمكنك أن ترى هنا أن موقع الذاكرة المستخدمة لكائن القلق (0x09ec0068) هو بالتأكيد بعيدا عن واحد من الكائن الأصلي (0xbf8e202f): المكدس، كالعادة، لديه عناوين عالية، في حين أن الذاكرة المستخدمة ل الكائن القيت إلى أسفل تماما في مساحة العنوان الافتراضية. ومع ذلك، فهذه تفاصيل التنفيذ، نظرا لأن الإجابات الأخرى أشارت إلى الخارج، فإن المعيار لا يقول أي شيء عن المكان الذي يجب أن يكون فيه الذاكرة عن كائن القيم وكيف ينبغي تخصيصه.

بالإضافة إلى ما تقوله المعيار في 15.1 / 4 ("استثناء مناولة / رمي استثناء") - يتم تخصيص الذاكرة الخاصة بالنسخة المؤقتة من الاستثناء الذي يتم إلقاؤه بطريقة غير محددة - بضعة أجزاء أخرى من التوافه حول كيف يتم تخصيص كائن استثناء هو:

  • يشير 3.7.3.1/4 ("وظائف التخصيص") من المعيار إلى أنه لا يمكن تخصيص كائن الاستثناء من قبل new التعبير أو مكالمة إلى "وظيفة تخصيص عالمية" (أي، operator new() إستبدال). لاحظ أن malloc() ليست "وظيفة تخصيص عالمية" كما هو محدد من قبل المعيار، لذلك malloc() هو بالتأكيد خيار لتخصيص كائن الاستثناء.

  • "عند إلقاء استثناء، يتم إنشاء كائن الاستثناء ووضعه بشكل عام على نوع من مكدس البيانات الاستثناء" (Stanley Lippman "(Stanley Lippman" (Stanley Lippman "(Stanley Lippman" (Stanley Lippman، "داخل طراز كائن C ++" - 7.2 معالجة استثناء)

  • من "لغة البرمجة C ++، الإصدار الثالث": "تطبيق C ++ مطلوب للحصول على ذاكرة احتياطية كافية لتكون قادرة على رمي bad_alloc في حالة استنفاد الذاكرة. ومع ذلك، فمن الممكن أن يؤدي إلقاء بعض الاستثناءات الأخرى إلى استنفاد الذاكرة. "(استنفاد الموارد 14.4.5)؛" يجوز للتنفيذ تطبيق مجموعة واسعة من الاستراتيجيات لتخزين الاستثناءات وإرسالها. ويضمن، ومع ذلك، فإن هناك ذاكرة كافية للسماح new لرمي استثناء قياسي خارج الذاكرة، bad_alloc"(14.3 استثناءات التقاط).

لاحظ أن عروض الأسعار من Stroustrup هي معيار مسبق. أجد أنه من المثير للاهتمام أن المعيار لا يبدو أن الضمان أن Strastrup يفكر مهم بما يكفي للذكر مرتين.

لأن المواصفات تنص صراحة، يتم إنشاء كائن مؤقت بدلا من throw المعامل.

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