هل هناك أي سبب يدعو إلى التحقق من مؤشر فارغة قبل حذف?

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

سؤال

كثيرا ما أرى تراث رمز التحقق NULL قبل حذف مؤشر مماثل ،

if (NULL != pSomeObject) 
{
    delete pSomeObject;
    pSomeObject = NULL;
}

هل هناك أي سبب يدعو إلى التحقق من وجود NULL المؤشر قبل حذف ذلك ؟ ما هو سبب وضع المؤشر NULL بعد ذلك ؟

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

المحلول

وانها تماما "آمنة" لحذف مؤشر فارغة. فهو يرقى فعليا إلى أي المرجع.

والسبب قد ترغب في التحقق من وجود باطل قبل حذف هو أن تحاول حذف مؤشر فارغة يمكن أن تشير إلى وجود خلل في البرنامج.

نصائح أخرى

C++ القياسية ضمانات قانونية استخدام مؤشر فارغة في حذف التعبير (§8.5.2.5/2).ومع ذلك ، غير محدد إذا كان هذا سوف اتصل deallocation وظيفة (operator delete أو operator delete[];§8.5.2.5/7 ، الحاشية).

إذا افتراضي deallocation وظيفة (أيالتي تقدمها المكتبة القياسية) ويسمى مع مؤشر فارغة, ثم الدعوة له أي تأثير (§6.6.4.4.2/3).

ولكنه غير محدد ماذا يحدث إذا deallocation وظيفة لا توفرها المكتبة القياسية — أيماذا يحدث عندما نقوم الزائد operator delete (أو operator delete[]).

المختصة مبرمج سيكون التعامل مع مؤشرات فارغة وفقا لذلك داخل على deallocation وظيفة بدلا من قبل الاتصال كما هو موضح في OP code.وبالمثل وضع المؤشر nullptr/NULL بعد حذف يخدم فقط محدودة جدا الغرض.بعض الناس مثل هذا في روح دفاعية البرمجة:أنها سوف تجعل البرنامج السلوك قليلا أكثر قابلية للتنبؤ في حالة من الخلل:الوصول إلى المؤشر بعد الحذف سيؤدي في مؤشر فارغة الوصول بدلا من الوصول إلى ذاكرة عشوائية الموقع.على الرغم من أن كل العمليات غير معروف السلوك سلوك مؤشر فارغة الوصول هو الكثير أكثر قابلية للتنبؤ في الممارسة (فإنه غالبا ما يؤدي مباشرة الحادث بدلا من الذاكرة الفساد).لأن الذاكرة الفساد خاصة التصحيح ، إعادة تعيين حذف مؤشرات الإيدز التصحيح.

— بالطبع هذا هو علاج الأعراض وليس السبب (أيعلة). يجب علاج إعادة المؤشرات كما رمز رائحة. نظيفة وحديثة C++ كود جعل الذاكرة ملكية واضحة بشكل ثابت فحص (باستخدام المؤشرات الذكية أو ما يعادلها الآليات) ، وبالتالي بصورة مبرهنة تجنب هذه الحالة.

مكافأة:شرح طاقتها operator delete:

operator delete هو (على الرغم من اسمها) وظيفة التي قد تكون مثقلة مثل أي وظيفة أخرى.هذه الوظيفة يحصل تسمى داخليا لكل دعوة operator delete مع مطابقة الحجج.نفس الشيء صحيح بالنسبة operator new.

الحمولة الزائدة operator new (وبعد ذلك أيضا operator delete) المنطقي في بعض الحالات عندما تريد التحكم بدقة كيف يتم تخصيص الذاكرة.القيام بذلك ليس من الصعب جدا ، ولكن بعض الاحتياطات يجب أن يتم لضمان تصحيح السلوك.سكوت مايرز يصف هذا بالتفصيل فعالة C++.

الآن دعنا نقول فقط أننا نريد أن تفرط في الإصدار العمومي من operator new من أجل التصحيح.قبل أن نفعل هذا واحد قصير حول ما يحدث في التعليمات البرمجية التالية:

klass* pobj = new klass;
// … use pobj.
delete pobj;

ما يحدث في الواقع هنا ؟ حسنا المذكورة أعلاه يمكن أن تترجم تقريبا إلى التعليمات البرمجية التالية:

// 1st step: allocate memory
klass* pobj = static_cast<klass*>(operator new(sizeof(klass)));
// 2nd step: construct object in that memory, using placement new:
new (pobj) klass();

// … use pobj.

// 3rd step: call destructor on pobj:
pobj->~klass();
// 4th step: free memory
operator delete(pobj);

إشعار الخطوة 2 حيث نسميه new مع قليلا الغريب الجملة.هذه هي الدعوة إلى ما يسمى التنسيب new الذي يأخذ عنوان ويبني كائن في هذا العنوان.هذا المشغل يمكن أن تكون مثقلة أيضا.في هذه الحالة, هو فقط يعمل على استدعاء منشئ الفئة klass.

الآن, دون مزيد من اللغط هنا هو رمز زائد نسخة من المشغلين:

void* operator new(size_t size) {
    // See Effective C++, Item 8 for an explanation.
    if (size == 0)
        size = 1;

    cerr << "Allocating " << size << " bytes of memory:";

    while (true) {
        void* ret = custom_malloc(size);

        if (ret != 0) {
            cerr << " @ " << ret << endl;
            return ret;
        }

        // Retrieve and call new handler, if available.
        new_handler handler = set_new_handler(0);
        set_new_handler(handler);

        if (handler == 0)
            throw bad_alloc();
        else
            (*handler)();
    }
}

void operator delete(void* p) {
    cerr << "Freeing pointer @ " << p << "." << endl;
    custom_free(p);
}

هذا الرمز يستخدم فقط مخصص تنفيذ malloc/free داخليا ، كما تفعل معظم التطبيقات.كما يخلق التصحيح الانتاج.النظر في التعليمات البرمجية التالية:

int main() {
    int* pi = new int(42);
    cout << *pi << endl;
    delete pi;
}

وله الإخراج التالي:

Allocating 4 bytes of memory: @ 0x100160
42
Freeing pointer @ 0x100160.

الآن, هذا الرمز يفعل شيئا مختلفا جذريا من معيار تنفيذ operator delete: لم يكن اختبار مؤشرات فارغة! المترجم لا تحقق هذا رمز أعلاه برمجيا ولكن قد تعطي سيئة الأخطاء في وقت التشغيل عند محاولة حذف مؤشرات فارغة.

ولكن كما قلت من قبل, هذا السلوك هو في الواقع غير متوقعة و مكتبة الكاتب يجب أن الحرص على التحقق من مؤشرات فارغة في operator delete.هذا الإصدار هو تحسنت كثيرا:

void operator delete(void* p) {
    if (p == 0) return;
    cerr << "Freeing pointer @ " << p << "." << endl;
    free(p);
}

في الختام, على الرغم من أن قذرة تنفيذ operator delete قد تتطلب صريحة null الشيكات في رمز العميل, هذا غير القياسية السلوك و يجب فقط أن التسامح في التراث الدعم (على كل حال).

وحذف الشيكات لNULL داخليا. الاختبار لا لزوم لها

وحذف باطل هو عدم المرجع. ليس هناك سبب للتحقق من باطل قبل استدعاء حذف.

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

ووفقا لC ++ 03 5.3.5 / 2، انها آمنة لحذف مؤشر فارغة. ونقل عن هذا التالية من المعيار:

<اقتباس فقرة>   

في أي بديل، إذا كانت قيمة المعامل من الحذف هي   لاغية مؤشر العملية أي تأثير.

إذا pSomeObject غير NULL، حذف لن تفعل أي شيء. لذلك لا، لم يكن لديك للتحقق من NULL.

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

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

وأعتقد المطور السابق ترميز أنه "بوفرة" لانقاذ بعض مللي ثانية: إنه لأمر جيد أن يكون يتم تعيين المؤشر إلى NULL على يتم حذف، لذلك يمكن استخدام خط مثل حق التالية بعد حذف الكائن:

if(pSomeObject1!=NULL) pSomeObject1=NULL;

ولكن بعد حذف يفعل ذلك مقارنة دقيقة على أي حال (لا تفعل شيئا إذا كان NULL). لماذا تفعل ذلك مرتين؟ يمكنك تعيين دائما pSomeObject إلى NULL بعد استدعاء حذف، بغض النظر عن قيمته الحالية - ولكن هذا سيكون زائدة قليلا إذا كان هذه القيمة بالفعل

وهكذا رهاني هو حاول المؤلف من تلك الخطوط لضمان أن pSomeObject1 تكون دائما فارغة بعد أن حذف، دون تكبد تكاليف اختبار يحتمل أن تكون غير ضرورية والرذيلة.

وذلك يعتمد على ما تقومون به. بعض التطبيقات القديمة من free، على سبيل المثال، لن تكون سعيدة إذا تم تمريرها أنها مؤشر NULL. بعض المكتبات لا تزال لديها هذه المشكلة. على سبيل المثال، XFree في المكتبة Xlib يقول:

<اقتباس فقرة>   

ووصف

     

وظيفة XFree هي   للأغراض العامة Xlib الروتينية التي   يحرر البيانات المحددة. يجب عليك أن   استخدامه لتحرير أي الكائنات التي كانت   خصصتها Xlib، إلا بديل   يتم تحديد وظيفة صراحة   الكائن. مؤشر NULL لا يمكن أن يكون   مرت لهذه المهمة.

وهكذا يدرس الافراج عن مؤشرات NULL بوصفها علة وعليك أن تكون آمنة.

وأما بالنسبة لملاحظاتي، وحذف مؤشر فارغة باستخدام حذف آمنة في آلات يونيكس مقرها آيك بيه-ريسك وإيتانيوم. ولكن غير آمنة تماما لأنظمة لينكس عملية شأنه ان تحطم ذلك الحين.

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