سؤال

class someclass {};

class base
{
    int a;
    int *pint;
    someclass objsomeclass;
    someclass* psomeclass;
public:
    base()
    {
        objsomeclass = someclass();
        psomeclass = new someclass();
        pint = new int(); 
        throw "constructor failed";
        a = 43;
    }
}

int main()
{
    base temp();
}

في التعليمات البرمجية أعلاه ، منشئ يلقي.الكائنات التي سيتم تسريب, و كيف يمكن تسرب الذاكرة تجنبها ؟

int main()
{
    base *temp = new base();
}

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

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

المحلول

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

هذا هو السبب الرئيسي لاستخدام المؤشرات الذكية - منذ الذكية poitners هي كاملة الكائنات, أنها سوف تحصل على destructors دعا خلال استثناء كومة والاسترخاء فرصة لتحرير الذاكرة.

إذا كنت تستخدم شيئا مثل دفعة scoped_ptr<> قالب الفئة الخاصة بك يمكن أن تبدو أكثر مثل:

class base{
    int a;
    scoped_ptr<int> pint;
    someclass objsomeclass;
    scoped_ptr<someclass> psomeclass;
    base() : 
       pint( new int),
       objsomeclass( someclass()),
       psomeclass( new someclass())

    {
        throw "constructor failed";
        a = 43;
    }
}

ولن يكون لديك أي تسرب الذاكرة (الافتراضي dtor أيضا بتنظيف الذاكرة الديناميكية المخصصات).


وخلاصة القول (و نأمل أن هذا أيضا يجيب على سؤال حول

base* temp = new base();

البيان):

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

  1. المدمر الكائن يجري بناؤها سوف لا أن يسمى.
  2. destructors الأعضاء الكائنات الواردة في هذا الفصل سوف يطلق
  3. ذاكرة الكائن الذي كان يجري بناؤها سيتم تحرير.

هذا يعني أنه إذا كان لديك كائن يملك الموارد لديك 2 الأساليب المتاحة لتنظيف تلك الموارد قد تم الحصول عليها عندما منشئ يلقي:

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

نصائح أخرى

كل جديد سوف يتم تسريبها.

تعيين عنوان كومة خلق الكائنات اسمه المؤشرات الذكية بحيث سيتم حذفه داخل مؤشرات الذكية المدمر التي تحصل على مكالمة عندما يتم طرح استثناء - (RAII).

class base {
    int a;
    boost::shared_ptr<int> pint;
    someclass objsomeclass;
    boost::shared_ptr<someclass> psomeclass;

    base() :
        objsomeclass( someclass() ),
        boost::shared_ptr<someclass> psomeclass( new someclass() ),
        boost::shared_ptr<int> pint( new int() )
    {
        throw "constructor failed";
        a = 43;
    }
};

الآن psomeclass & لتر destructors سوف يتم استدعاؤها عند كومة الاسترخاء عندما يتم طرح استثناء في منشئ ، وتلك destructors سيتم تخصيص الذاكرة المخصصة.

int main(){
    base *temp = new base();
}

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

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

هذا برنامج بسيط يوضح ذلك.

#include <stdio.h>


class A
{
    int x;

public:
    A(int x) : x(x) { printf("A constructor [%d]\n", x); }
    ~A() { printf("A destructor [%d]\n", x); }
};


class B
{
    A a1;
    A a2;

public:
    B()
    :   a1(3),
        a2(5)
    {
        printf("B constructor\n");
        throw "failed";
    }
    ~B() { printf("B destructor\n"); }
};


int main()
{
    B b;

    return 0;
}

مع الإخراج التالي (باستخدام g++ 4.5.2):

A constructor [3]
A constructor [5]
B constructor
terminate called after throwing an instance of 'char const*'
Aborted

إذا كان منشئ فشل جزئيا ثم تتحمل مسؤولية التعامل معها.أسوأ من ذلك ، قد يكون استثناء القيت من الفئة الأساسية' منشئ!طريقة للتعامل مع هذه الحالات عن طريق استخدام وظيفة "محاولة كتلة" (ولكن حتى ذلك الحين يجب أن بعناية رمز تدمير جزئي تهيئة كائن).

النهج الصحيح للمشكلة ثم يكون شيئا من هذا القبيل:

#include <stdio.h>


class A
{
    int x;

public:
    A(int x) : x(x) { printf("A constructor [%d]\n", x); }
    ~A() { printf("A destructor [%d]\n", x); }
};


class B
{
    A * a1;
    A * a2;

public:
    B()
    try  // <--- Notice this change
    :   a1(NULL),
        a2(NULL)
    {
        printf("B constructor\n");
        a1 = new A(3);
        throw "fail";
        a2 = new A(5);
    }
    catch ( ... ) {   // <--- Notice this change
        printf("B Cleanup\n");
        delete a2;  // It's ok if it's NULL.
        delete a1;  // It's ok if it's NULL.
    }

    ~B() { printf("B destructor\n"); }
};


int main()
{
    B b;

    return 0;
}

إذا كنت تشغيله سوف تحصل على الناتج المتوقع حيث لا يوجد سوى تخصص الكائنات تدميرها و تحرير.

B constructor
A constructor [3]
B Cleanup
A destructor [3]
terminate called after throwing an instance of 'char const*'
Aborted

لا يزال يمكنك العمل بها مع الذكية مشترك مؤشرات إذا كنت ترغب في, مع المزيد من النسخ.كتابة منشئ مشابهة لهذه:

class C
{
    std::shared_ptr<someclass> a1;
    std::shared_ptr<someclass> a2;

public:
    C()
    {
        std::shared_ptr<someclass> new_a1(new someclass());
        std::shared_ptr<someclass> new_a2(new someclass());

        // You will reach here only if both allocations succeeded. Exception will free them both since they were allocated as automatic variables on the stack.
        a1 = new_a1;
        a2 = new_a2;
    }
}

حظا سعيدا ، تسيفي.

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

نعم هذا الكود تسرب الذاكرة.كتل من الذاكرة المخصصة باستخدام "الجديد" لم يتم تحرير عندما يتم رفع استثناء.هذا هو جزء من الدافع وراء RAII.

لتجنب تسرب الذاكرة ، في محاولة شيء من هذا القبيل:

psomeclass = NULL;
pint = NULL;
/* So on for any pointers you allocate */

try {
    objsomeclass = someclass();
    psomeclass = new someclass();
    pint = new int(); 
    throw "constructor failed";
    a = 43;
 }
 catch (...)
 {
     delete psomeclass;
     delete pint;
     throw;
 }

كل "جديد" يحتاج إلى حذف ، أو سوف يسبب تسرب الذاكرة.حتى هذه خطين:

psomeclass = new someclass();
pint = new int(); 

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

delete pint;
delete psomeclass;

في النهاية كتلة لتجنب لهم يجري تسريبه.

أيضا, هذا السطر:

base temp = base();

هو لا لزوم لها.تحتاج فقط إلى القيام به:

base temp;

مضيفا "= قاعدة()" لا لزوم لها.

تحتاج إلى حذف psomeclass...ليس ضروريا لتنظيف صحيح...

RWendi

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