هل سيؤدي رمز C++ هذا إلى تسرب الذاكرة (مجموعة الصب الجديدة)

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

سؤال

لقد كنت أعمل على بعض أكواد C++ القديمة التي تستخدم بنيات متغيرة الطول (TAPI)، حيث يعتمد حجم البنية على سلاسل متغيرة الطول.يتم تخصيص الهياكل عن طريق مجموعة الصب new هكذا:

STRUCT* pStruct = (STRUCT*)new BYTE [sizeof(STRUCT) + nPaddingSize];

ولكن في وقت لاحق يتم تحرير الذاكرة باستخدام ملف delete يتصل:

delete pStruct;

هل هذا المزيج من المصفوفة new [] وغير المصفوفة delete يسبب تسرب للذاكرة أم أن ذلك يعتمد على المترجم؟هل سيكون من الأفضل تغيير هذا الرمز لاستخدامه malloc و free بدلاً من؟

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

المحلول

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

والأهم إذا STRUCT عندما يكون لديك (أو يتم إعطاؤه) مدمرًا، فإنه سيتم استدعاء المدمر دون استدعاء المنشئ المقابل.

بالطبع، إذا كنت تعرف من أين جاء pStruct، فلماذا لا تقوم فقط بإدراجه عند الحذف لمطابقة التخصيص:

delete [] (BYTE*) pStruct;

نصائح أخرى

أنا شخصياً أعتقد أنه من الأفضل أن تستخدمه std::vector لإدارة ذاكرتك، لذلك لا تحتاج إلى delete.

std::vector<BYTE> backing(sizeof(STRUCT) + nPaddingSize);
STRUCT* pStruct = (STRUCT*)(&backing[0]);

بمجرد أن يترك الدعم المجال، لديك pStruct لم يعد صالحا.

أو يمكنك استخدام:

boost::scoped_array<BYTE> backing(new BYTE[sizeof(STRUCT) + nPaddingSize]);
STRUCT* pStruct = (STRUCT*)backing.get();

أو boost::shared_array إذا كنت بحاجة إلى نقل الملكية.

نعم سوف يسبب تسرب للذاكرة.

شاهد هذا باستثناء C++ Gotchas: http://www.informit.com/articles/article.aspx?p=30642 لماذا.

لدى ريموند تشين شرح لكيفية النواقل new و delete تختلف عن الإصدارات العددية تحت الأغطية الخاصة بمترجم Microsoft ...هنا:http://blogs.msdn.com/oldnewthing/archive/2004/02/03/66660.aspx

IMHO يجب عليك إصلاح الحذف إلى:

delete [] pStruct;

بدلا من التحول إلى malloc/free, ، فقط لأنه تغيير أبسط يمكن إجراؤه دون ارتكاب أخطاء ;)

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

delete [] reinterpret_cast<BYTE *>(pStruct);

لذا، أعتقد أنه من السهل التبديل إليه malloc/free بعد كل ذلك ؛)

سلوك الكود غير محدد.قد تكون محظوظًا (أو لا) وقد يعمل مع برنامج التحويل البرمجي الخاص بك، ولكن في الحقيقة هذا ليس رمزًا صحيحًا.هناك مشكلتان في ذلك:

  1. ال delete يجب أن تكون مصفوفة delete [].
  2. ال delete يجب أن يتم استدعاؤه على المؤشر إلى نفس النوع مثل النوع المخصص.

لكي تكون صحيحًا تمامًا، فأنت تريد أن تفعل شيئًا مثل هذا:

delete [] (BYTE*)(pStruct);

ينص معيار C++ بوضوح على ما يلي:

delete-expression:
             ::opt delete cast-expression
             ::opt delete [ ] cast-expression

البديل الأول مخصص للكائنات غير المصفوفة، والثاني مخصص للمصفوفات.يجب أن يكون للمعامل نوع مؤشر، أو نوع فئة له وظيفة تحويل واحدة (12.3.2) إلى نوع مؤشر.النتيجة لها نوع فارغ.

في البديل الأول (حذف الكائن)، يجب أن تكون قيمة معامل الحذف مؤشرًا لكائن غير مصفوفة [...] وإذا لم يكن الأمر كذلك، يكون السلوك غير محدد.

قيمة المعامل في delete pStruct هو مؤشر لمجموعة من char, ، مستقلة عن نوعها الثابت (STRUCT*).لذلك، فإن أي مناقشة حول تسرب الذاكرة لا طائل من ورائها، لأن التعليمات البرمجية غير جيدة الصياغة، ولا يلزم وجود مترجم C++ لإنتاج ملف قابل للتنفيذ معقول في هذه الحالة.

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

كما هو موضح في مشاركات أخرى:

1) استدعاءات جديدة/حذف تخصيص الذاكرة وقد تستدعي المنشئين/المدمرين (C++ '03 5.3.4/5.3.5)

2) خلط إصدارات المصفوفة/غير المصفوفة من new و delete هو سلوك غير محدد.(C++ '03 5.3.5/4)

بالنظر إلى المصدر يبدو أن شخصًا ما قام بالبحث والاستبدال malloc و free وما سبق هو النتيجة.يحتوي C++ على بديل مباشر لهذه الوظائف، وهو استدعاء وظائف التخصيص new و delete مباشرة:

STRUCT* pStruct = (STRUCT*)::operator new (sizeof(STRUCT) + nPaddingSize);
// ...
pStruct->~STRUCT ();  // Call STRUCT destructor
::operator delete (pStruct);

إذا كان يجب استدعاء مُنشئ STRUCT، فيمكنك التفكير في تخصيص الذاكرة ثم استخدام الموضع new:

BYTE * pByteData = new BYTE[sizeof(STRUCT) + nPaddingSize];
STRUCT * pStruct = new (pByteData) STRUCT ();
// ...
pStruct->~STRUCT ();
delete[] pByteData;

@ اريك - شكرا على التعليقات.رغم ذلك، تستمر في قول شيء يدفعني للجنون:

تتعامل مكتبات وقت التشغيل هذه مع مكالمات إدارة الذاكرة إلى نظام التشغيل في بناء جملة ثابتة مستقلة ومكتبات وقت التشغيل مسؤولة عن جعل Malloc والعمل الجديد باستمرار بين Ons و Windows و Solaris و AIX ، إلخ ... .

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

يتم توفير التوافق لأن واجهة برمجة التطبيقات الخاصة بـ std، وما إلى ذلك.هو نفسه - ليس لأن مكتبات وقت التشغيل جميعها تستدير وتستدعي نفس مكالمات نظام التشغيل بالضبط.

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

في كثير من الأحيان يتم تنفيذ هاتين الخطوتين بواسطة عبارة C++ واحدة.

MyObject* ObjPtr = new MyObject;

//...

delete MyObject;

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

void* MemoryPtr = ::operator new( sizeof(MyObject) );
MyObject* ObjPtr = new (MemoryPtr) MyObject;

// ...

ObjPtr->~MyObject();
::operator delete( MemoryPtr );

لاحظ أنه لا يوجد أي عملية نقل، ويتم إنشاء نوع واحد فقط من الكائنات في منطقة الذاكرة المخصصة.باستخدام شيء من هذا القبيل new char[N] كطريقة لتخصيص الذاكرة الخام غير صحيحة من الناحية الفنية، فمن المنطقي، char يتم إنشاء الكائنات في الذاكرة المخصصة حديثًا.لا أعرف أي موقف لا "يعمل فيه فقط" ولكنه يطمس التمييز بين تخصيص الذاكرة الأولية وإنشاء الكائنات، لذا أنصح بعدم القيام بذلك.

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

class MyObject
{
    void* operator new( std::size_t rqsize, std::size_t padding )
    {
        return ::operator new( rqsize + padding );
    }

    // Usual (non-placement) delete
    // We need to define this as our placement operator delete
    // function happens to have one of the allowed signatures for
    // a non-placement operator delete
    void operator delete( void* p )
    {
        ::operator delete( p );
    }

    // Placement operator delete
    void operator delete( void* p, std::size_t )
    {
        ::operator delete( p );
    }
};

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

// Called in one step like so:
MyObject* ObjectPtr = new (padding) MyObject;

باستخدام تعبير واحد جديد، نضمن الآن عدم تسرب الذاكرة في حالة ظهور أي جزء من التعبير الجديد.

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

delete ObjectPtr;

ملخص!

  1. لا تنظر إلى الممثلين! operator new و operator delete التعامل مع الذاكرة الأولية، حيث يمكن للموضع الجديد إنشاء كائنات في الذاكرة الأولية.طاقم واضح من أ void* عادةً ما يكون مؤشر الكائن علامة على وجود شيء خاطئ منطقيًا، حتى لو كان "يعمل فقط".

  2. لقد تجاهلنا تمامًا new[] وحذفنا[].لن تعمل هذه الكائنات ذات الحجم المتغير في المصفوفات بأي حال من الأحوال.

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

اذا أنت حقًا يجب أن تفعل هذا النوع من الأشياء، ربما يجب عليك الاتصال بالمشغل new مباشرة:

STRUCT* pStruct = operator new(sizeof(STRUCT) + nPaddingSize);

أعتقد أن تسميتها بهذه الطريقة يتجنب استدعاء المنشئين/المدمرين.

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

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

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

تمت تغطية هذا النوع من الأشياء بشكل جيد في الأسئلة الشائعة حول C++ (الأقسام 16.12 و16.13 و16.14):

http://www.parashift.com/c++-faq-lite/freestore-mgmt.html#faq-16.12

إنها عبارة عن مصفوفة حذف ([]) التي تشير إليها، وليست حذفًا متجهًا.المتجه هو std::vector، ويهتم بحذف عناصره.

يمكنك الرجوع إلى BYTE * والحذف:

delete[] (BYTE*)pStruct;

نعم قد يكون ذلك، نظرًا لأن التخصيص باستخدام new[] ولكن إلغاء التخصيص باستخدام delelte، نعم يعد malloc/free أكثر أمانًا هنا، ولكن في c++، يجب ألا تستخدمها لأنها لن تتعامل مع المُنشئين (de).

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

سيكون من الأفضل القيام بذلك بشكل صحيح، حيث سيؤدي هذا أيضًا إلى استدعاء أي منشئين ومفككين بشكل صحيح

STRUCT* pStruct = new STRUCT;
...
delete pStruct;

من الأفضل دائمًا الحفاظ على توازن عملية الحصول على/إصدار أي مورد قدر الإمكان.على الرغم من أنه من الصعب القول تسرب أم لا في هذه الحالة.يعتمد ذلك على تنفيذ المترجم لتخصيص المتجه.

BYTE * pBytes = new BYTE [sizeof(STRUCT) + nPaddingSize];

STRUCT* pStruct = reinterpret_cast< STRUCT* > ( pBytes ) ;

 // do stuff with pStruct

delete [] pBytes ;

لين:المشكلة في ذلك هي أن pStruct عبارة عن STRUCT*، لكن الذاكرة المخصصة هي في الواقع BYTE[] ذات حجم غير معروف.لذا، لن يؤدي حذف [] pStruct إلى إلغاء تخصيص كل الذاكرة المخصصة.

أنت نوع من المزج بين طرق C وC++ لفعل الأشياء.لماذا تخصيص أكثر من حجم STRUCT؟لماذا لا يقتصر الأمر على "الهيكل الجديد"؟إذا كان يجب عليك القيام بذلك، فقد يكون من الواضح استخدام malloc وfree في هذه الحالة، حيث أنه بعد ذلك قد تكون أنت أو المبرمجون الآخرون أقل احتمالية لوضع افتراضات حول أنواع وأحجام الكائنات المخصصة.

matt cruikshank يجب أن تولي اهتمامًا وقراءة ما كتبته مرة أخرى لأنني لم أقترح أبدًا عدم الاتصال بـ DELETE [] والسماح لنظام التشغيل بالتنظيف.وأنت مخطئ بشأن إدارة مكتبات وقت التشغيل C++ للكومة.إذا كان الأمر كذلك، فلن يكون C++ محمولاً كما هو الحال اليوم ولن يتم تنظيف التطبيق المتعطل أبدًا بواسطة نظام التشغيل.(مع الإقرار بوجود أوقات تشغيل محددة لنظام التشغيل تجعل C/C++ تبدو غير محمولة).أتحداك أن تجد stdlib.h في مصادر Linux من kernel.org.الكلمة الأساسية الجديدة في C++ تتحدث في الواقع إلى نفس إجراءات إدارة الذاكرة مثل malloc.

تقوم مكتبات وقت التشغيل C++ بإجراء مكالمات نظام التشغيل ونظام التشغيل هو الذي يدير الأكوام.أنت على حق جزئيًا في أن مكتبات وقت التشغيل تشير إلى وقت تحرير الذاكرة، ومع ذلك، فهي لا تنتقل فعليًا إلى أي جداول كومة الذاكرة المؤقتة مباشرة.بمعنى آخر، لا يضيف وقت التشغيل الذي ترتبط به تعليمات برمجية إلى التطبيق الخاص بك للسير في أكوام من التخصيص أو إلغاء التخصيص.هذا هو الحال في أنظمة التشغيل Windows وLinux وSolaris وAIX وما إلى ذلك...وهذا أيضًا هو السبب وراء عدم قيامك بغرامة malloc في أي مصدر نواة Linux ولن تجد stdlib.h في مصدر Linux.افهم أن أنظمة التشغيل الحديثة هذه تحتوي على مديري ذاكرة افتراضية مما يزيد من تعقيد الأمور قليلاً.

هل تساءلت يومًا لماذا يمكنك إجراء اتصال بـ malloc للحصول على 2G من ذاكرة الوصول العشوائي (RAM) على صندوق 1G مع الاستمرار في استعادة مؤشر ذاكرة صالح؟

تتم إدارة إدارة الذاكرة على معالجات x86 ضمن مساحة Kernel باستخدام ثلاثة جداول.PAM (جدول تخصيص الصفحات)، PD (أدلة الصفحات)، وPT (جداول الصفحات).هذا على مستوى الأجهزة الذي أتحدث عنه.أحد الأشياء التي يقوم بها مدير ذاكرة نظام التشغيل، وليس تطبيق C++ الخاص بك، هو معرفة مقدار الذاكرة الفعلية المثبتة على الصندوق أثناء التمهيد بمساعدة مكالمات BIOS.يتعامل نظام التشغيل أيضًا مع الاستثناءات، كما هو الحال عند محاولة الوصول إلى الذاكرة، حيث لا يتمتع التطبيق الخاص بك بحقوق أيضًا.(خطأ الحماية العامة في GPF).

ربما نقول نفس الشيء يا مات، لكنني أعتقد أنك قد تربك وظيفة الغطاء السفلي قليلاً.أستخدمه للحفاظ على مترجم C/C++ لقمة العيش ...

@ericmayo - كريبس.حسنًا، عند تجربة VS2005، لا يمكنني الحصول على تسرب صادق من الحذف العددي في الذاكرة الذي تم إجراؤه بواسطة ناقل جديد.أعتقد أن سلوك المترجم "غير محدد" هنا، وهو أفضل دفاع يمكنني حشده.

ومع ذلك، عليك أن تعترف بأنه من الممارسات الرديئة حقًا أن تفعل ما قاله الملصق الأصلي.

إذا كان هذا هو الحال ، فلن يكون C ++ محمولًا كما هو اليوم ولن يتم تنظيف تطبيق التحطم أبدًا بواسطة نظام التشغيل.

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

@ مات كروكشانك

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

لا أوافق على أنه سلوك مترجم أو حتى مشكلة مترجم.يتم تجميع الكلمة الأساسية "الجديدة" وربطها، كما أشرت، بمكتبات وقت التشغيل.تتعامل مكتبات وقت التشغيل هذه مع مكالمات إدارة الذاكرة لنظام التشغيل في بناء جملة ثابت ومستقل لنظام التشغيل ومكتبات وقت التشغيل هذه مسؤولة عن إنشاء malloc والعمل الجديد بشكل متسق بين أنظمة تشغيل مثل Linux وWindows وSolaris وAIX وما إلى ذلك... .هذا هو السبب الذي جعلني أذكر حجة قابلية النقل؛محاولة لإثبات أن وقت التشغيل لا يقوم فعليًا بإدارة الذاكرة أيضًا.

يقوم نظام التشغيل بإدارة الذاكرة.

واجهة libs وقت التشغيل لنظام التشغيل.على نظام التشغيل Windows، هذا هو مدير الذاكرة الظاهرية DLLs.ولهذا السبب يتم تنفيذ stdlib.h ضمن مكتبات GLIB-C وليس مصدر Linux kernel؛إذا تم استخدام GLIB-C في أنظمة تشغيل أخرى، فهذا يعني تنفيذ تغييرات malloc لإجراء استدعاءات نظام التشغيل الصحيحة.في VS، بورلاند، الخ.لن تجد أبدًا أي مكتبات تأتي مع المترجمين الخاصين بها والتي تدير الذاكرة بالفعل أيضًا.ومع ذلك، ستجد تعريفات محددة لنظام التشغيل لـ malloc.

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

ولا تأخذها مني.اقرأ الكود المصدري لنظام التشغيل Linux أو يمكنك رؤية ما يقوله K&R عنه...إليك رابط PDF إلى K&R على C.

http://www.oberon2005.ru/paper/kr_c.pdf

انظر قرب نهاية الصفحة 149:"يمكن إجراء المكالمات إلى malloc وfree بأي ترتيب؛Malloc يدعو نظام التشغيل للحصول على مزيد من الذاكرة حسب الضرورة.توضح هذه الإجراءات بعض الاعتبارات المتعلقة بكتابة التعليمات البرمجية المعتمدة على الآلة بطريقة مستقلة نسبيًا عن الآلة، كما تُظهر أيضًا تطبيقًا حقيقيًا للهياكل والاتحادات والكتابات المكتوبة.

"عليك أن تعترف رغم ذلك، أنها ممارسة سيئة حقًا أن تفعل ما قاله الملصق الأصلي."

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

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

علاوة على ذلك، كان سبب ردي كما فعلت هو تعليق OP "هياكل متغيرة الطول (TAPI)، حيث سيعتمد حجم البنية على سلاسل متغيرة الطول"

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

بالإضافة إلى الإجابات الممتازة أعلاه، أود أيضًا أن أضيف:

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

استخدم عامل التشغيل الجديد واحذف:

struct STRUCT
{
  void *operator new (size_t)
  {
    return new char [sizeof(STRUCT) + nPaddingSize];
  }

  void operator delete (void *memory)
  {
    delete [] reinterpret_cast <char *> (memory);
  }
};

void main()
{
  STRUCT *s = new STRUCT;
  delete s;
}

أعتقد أنه لا يوجد تسرب للذاكرة.

STRUCT* pStruct = (STRUCT*)new BYTE [sizeof(STRUCT) + nPaddingSize];

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

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

كما ترى، فإن مترجم C/C++ لا يدير الذاكرة، بل نظام التشغيل الأساسي هو الذي يديرها.

أوافق على أن هناك طرقًا أنظف لكن البروتوكول الاختياري قال إن هذا رمز قديم.

باختصار، لا أرى تسربًا للذاكرة لأن الإجابة المقبولة تعتقد بوجود تسرب.

روب ووكر رد جيد.

مجرد إضافة صغيرة، إذا لم يكن لديك أي مُنشئ و/أو مُدمر، لذلك تحتاج بشكل أساسي إلى تخصيص وتحرير جزء من الذاكرة الأولية، ففكر في استخدام زوج مجاني/مالوك.

موقع ericmayo.myopenid.com مخطئ جدًا، لدرجة أنه يجب على أي شخص يتمتع بسمعة كافية أن يصوت ضده.

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

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