سؤال

هناك طريقة تسمى foo الذي يُرجع أحيانًا الخطأ التالي:

terminate called after throwing an instance of 'std::bad_alloc'
  what():  std::bad_alloc
Abort

هل هناك طريقة يمكنني من خلالها استخدام أ try-catch block لإيقاف هذا الخطأ من إنهاء برنامجي (كل ما أريد فعله هو العودة -1)?

إذا كان الأمر كذلك، ما هو بناء الجملة لذلك؟

وإلا كيف يمكنني التعامل معها bad_alloc في C++؟

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

المحلول

يمكنك التقاطها مثل أي استثناء آخر: giveacodicetagpre.

تماما ما يمكنك القيام به بشكل مفيد من هذه النقطة متروك لك، لكنه أمر ممكن بالتأكيد تقنيا.

نصائح أخرى

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

والأسوأ من ذلك أن أنظمة التشغيل الحديثة غالبًا ما تفرط في التخصيص:على مثل هذه الأنظمة malloc و new يمكن أن يكون مؤشرًا صالحًا حتى لو لم تكن هناك ذاكرة خالية كافية – std::bad_alloc لن يتم إلقاؤها أبدًا، أو على الأقل ليست علامة موثوقة على استنفاد الذاكرة.بدلا من ذلك، محاولات وصول ستؤدي الذاكرة المخصصة بعد ذلك إلى خطأ تجزئة، وهو أمر لا يمكن اكتشافه (يمكنك ذلك مقبض إشارة خطأ التجزئة، ولكن لا يمكنك استئناف البرنامج بعد ذلك).

الشيء الوحيد الذي يمكنك فعله عند الإمساك std::bad_alloc ربما يكون تسجيل الخطأ ومحاولة ضمان إنهاء آمن للبرنامج عن طريق تحرير الموارد المعلقة (ولكن يتم ذلك تلقائيًا في المسار الطبيعي لتفكيك المكدس بعد ظهور الخطأ إذا كان البرنامج يستخدم RAII بشكل مناسب).

في حالات معينة، قد يحاول البرنامج تحرير بعض الذاكرة والمحاولة مرة أخرى، أو استخدام الذاكرة الثانوية (= القرص) بدلاً من ذاكرة الوصول العشوائي (RAM) ولكن هذه الفرص موجودة فقط في سيناريوهات محددة للغاية وبشروط صارمة:

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

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

حسنًا، لنفترض أنك حددت النقطة رقم 1.ما يمكنك فعله الآن هو على سبيل المثال استخدام ملف ذاكرة التخزين المؤقت LRU لبعض بياناتك (ربما بعض كائنات الأعمال الكبيرة بشكل خاص والتي يمكن إعادة إنشائها أو إعادة تحميلها عند الطلب).بعد ذلك، تحتاج إلى وضع المنطق الفعلي الذي قد يفشل في وظيفة تدعم إعادة المحاولة - وبعبارة أخرى، إذا تم إحباطها، يمكنك فقط إعادة تشغيلها:

lru_cache<widget> widget_cache;

double perform_operation(int widget_id) {
    std::optional<widget> maybe_widget = widget_cache.find_by_id(widget_id);
    if (not maybe_widget) {
        maybe_widget = widget_cache.store(widget_id, load_widget_from_disk(widget_id));
    }
    return maybe_widget->frobnicate();
}

…

for (int num_attempts = 0; num_attempts < MAX_NUM_ATTEMPTS; ++num_attempts) {
    try {
        return perform_operation(widget_id);
    } catch (std::bad_alloc const&) {
        if (widget_cache.empty()) throw; // memory error elsewhere.
        widget_cache.remove_oldest();
    }
}

// Handle too many failed attempts here.

ولكن حتى هنا، باستخدام std::set_new_handler بدلا من التعامل std::bad_alloc يوفر نفس الفائدة وسيكون أبسط من ذلك بكثير.


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

ما هو السلوك المحدد لمعيار C++؟ new في ج ++؟

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

C++03 القسم 3.7.4.1.3: يقول

يمكن لوظيفة التخصيص التي تفشل في تخصيص مساحة تخزين استدعاء new_handler(18.4.2.2) المثبت حاليًا، إن وجد.[ملحوظة:يمكن لوظيفة التخصيص التي يوفرها البرنامج الحصول على عنوان new_handler المثبت حاليًا باستخدام وظيفة set_new_handler (18.4.2.3).] إذا تم الإعلان عن وظيفة تخصيص بمواصفات استثناء فارغة (15.4)، فشل throw() في تخصيص التخزين، يجب أن يعود مؤشر فارغ.أي وظيفة تخصيص أخرى تفشل في تخصيص مساحة تخزين يجب أن تشير فقط إلى الفشل عن طريق طرح استثناء للفئة std::bad_alloc (18.4.2.1) أو فئة مشتقة من std::bad_alloc.

خذ بعين الاعتبار نموذج التعليمات البرمجية التالي:

#include <iostream>
#include <cstdlib>

// function to call if operator new can't allocate enough memory or error arises
void outOfMemHandler()
{
    std::cerr << "Unable to satisfy request for memory\n";

    std::abort();
}

int main()
{
    //set the new_handler
    std::set_new_handler(outOfMemHandler);

    //Request huge memory size, that will cause ::operator new to fail
    int *pBigDataArray = new int[100000000L];

    return 0;
}

في المثال أعلاه، operator new (على الأرجح) لن يكون قادرًا على تخصيص مساحة لـ 100.000.000 عدد صحيح، والدالة outOfMemHandler() سيتم استدعاؤه، وسيتم إحباط البرنامج بعد ذلك إصدار رسالة خطأ.

كما رأينا هنا السلوك الافتراضي لـ new عندما يتعذر على المشغل تلبية طلب الذاكرة، يجب الاتصال بـ new-handler تعمل بشكل متكرر حتى تتمكن من العثور على ذاكرة كافية أو لا يوجد المزيد من المعالجات الجديدة.في المثال أعلاه، ما لم نتصل std::abort(), outOfMemHandler() سيكون دعا مرارا وتكرارا.لذلك، يجب على المعالج إما التأكد من نجاح التخصيص التالي، أو تسجيل معالج آخر، أو عدم تسجيل أي معالج، أو عدم العودة (على سبيل المثال.إنهاء البرنامج).إذا لم يكن هناك معالج جديد وفشل التخصيص، فسيطرح عامل التشغيل استثناءً.

ما هو new_handler و set_new_handler?

new_handler هو typedef لمؤشر إلى دالة لا تأخذ ولا تُرجع شيئًا، و set_new_handler هي دالة تأخذ وترجع a new_handler.

شيء مثل:

typedef void (*new_handler)();
new_handler set_new_handler(new_handler p) throw();

تعد معلمة set_new_handler بمثابة مؤشر لمشغل الوظيفة new يجب الاتصال إذا لم يتمكن من تخصيص الذاكرة المطلوبة.قيمة الإرجاع الخاصة بها هي مؤشر إلى وظيفة المعالج المسجلة مسبقًا، أو فارغة إذا لم يكن هناك معالج سابق.

كيفية التعامل مع حالات نفاد الذاكرة في C++؟

نظرا لسلوك newيجب أن يتعامل برنامج المستخدم المصمم جيدًا مع حالات نفاد الذاكرة من خلال توفير حل مناسب new_handlerوالذي يقوم بأحد الإجراءات التالية:

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

قم بتثبيت معالج جديد مختلف: إذا لم يتمكن المعالج الجديد الحالي من توفير المزيد من الذاكرة، وكان هناك معالج جديد آخر يمكنه ذلك، فيمكن للمعالج الجديد الحالي تثبيت المعالج الجديد الآخر في مكانه (عن طريق الاتصال set_new_handler).في المرة التالية التي يستدعي فيها عامل التشغيل new وظيفة المعالج الجديد، سيحصل على الوظيفة التي تم تثبيتها مؤخرًا.

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

قم بإلغاء تثبيت المعالج الجديد: يتم ذلك عن طريق تمرير مؤشر فارغ إلى set_new_handler.مع عدم تثبيت معالج جديد، operator new سوف يلقي استثناء ((للتحويل إلى) std::bad_alloc) عندما يكون تخصيص الذاكرة غير ناجح.

رمي استثناء للتحويل إلى std::bad_alloc.لا يمكن اكتشاف مثل هذه الاستثناءات operator new, ، ولكن سيتم نشره إلى الموقع الذي أنشأ طلب الذاكرة.

لا ترجع: بالاتصال abort أو exit.

أنا لا أقترح هذا، منذ ذلك الحين bad_alloc يعني أنت خارج الذاكرة.سيكون من الأفضل الاستسلام بدلاً من محاولة التعافي.ولكن هنا هو الحل الذي تطلبه:

try {
    foo();
} catch ( const std::bad_alloc& e ) {
    return -1;
}

قد أقترح حل أكثر بساطة (وحتى أسرع) لهذا.سيعود مشغل GiveAcodicetAchode NULL إذا تعذر تخصيص الذاكرة. giveacodicetagpre.

آمل أن يساعد هذا!

اسمحوا الخاص بك فو البرنامج خروج في طريقة التحكم:

#include <stdlib.h>     /* exit, EXIT_FAILURE */

try {
    foo();
} catch (const std::bad_alloc&) {
    exit(EXIT_FAILURE);
}

ثم كتابة شل البرنامج يدعو البرنامج الفعلي.منذ مساحات العناوين هي فصل الدولة من shell الخاص بك البرنامج هو دائما واضحة المعالم.

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