سؤال

لقد أمضيت السنوات الأربع الماضية في لغة C#، لذا فأنا مهتم بأفضل الممارسات الحالية وأنماط التصميم الشائعة في لغة C++.خذ بعين الاعتبار المثال الجزئي التالي:

class World
{
public:
    void Add(Object *object);
    void Remove(Object *object);
    void Update();
}

class Fire : Object
{
public:
    virtual void Update()
    {
        if(age > burnTime)
        {
            world.Remove(this);
            delete this;
        }
    }
}

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

هل هذا أمر معقول أم أن هناك تصميمًا أفضل يساعد في تنظيف هذه الأشياء؟

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

المحلول

والمشكلة مع هذا هي أنك تقوم بإنشاء حقا اقتران ضمني بين الكائن والطراز العالمي.

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

في عام، أود أن أقول أنه هو unintuitive جدا لتحديث وظيفة () لحذف الكائن هو التحديث. انا اقول كما انها unintuitive لكائن لحذف نفسها. يجب أن أكثر المرجح أن يكون الكائن نوع من طريقة لإطلاق حدث قائلا إن انتهائه حرق، والمهتمين يمكن أن تعمل الآن على ذلك. على سبيل المثال عن طريق إزالته من العالم. لحذف ذلك، واعتقد من حيث الملكية.

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

ولكن إذا لم يكن هناك مالك واحد واضح المعالم، وتنفيذ الملكية المشتركة، على سبيل المثال باستخدام مؤشر الذكية تنفيذ العد المرجعية، مثل boost::shared_ptr

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

نصائح أخرى

ولقد جعلت Update دالة ظاهري، مما يشير إلى أن الفئات المشتقة قد تجاوز تنفيذ Update. هذا يدخل اثنين من مخاطر كبيرة.

1) قد يتذكر تنفيذا متجاوزة للقيام World.Remove، ولكن قد ينسى delete this. وتسربت الذاكرة.

2). ويدعو تنفيذ متجاوزة للUpdate من الدرجة القاعدة التي يقوم delete this، ولكن ثم تنتقل مع المزيد من العمل، ولكن مع صالح هذا المؤشر.

وجرب هذا المثال:

class WizardsFire: public Fire
{
public:
    virtual void Update()
    {
        Fire::Update();  // May delete the this-object!
        RegenerateMana(this.ManaCost); // this is now invaild!  GPF!
    }
}

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

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

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

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

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

بعض الأفكار حول هذا

  1. أضف قائمة بمراجع الكائنات إلى World.كل كائن في تلك القائمة معلق للإزالة.يعد هذا أسلوبًا شائعًا، ويتم استخدامه في wxWidgets لنوافذ المستوى الأعلى التي تم إغلاقها، ولكن قد يستمر تلقي الرسائل.في وقت الخمول، عندما تتم معالجة كافة الرسائل، تتم معالجة القائمة المعلقة، ويتم حذف الكائنات.أعتقد أن Qt يتبع أسلوبًا مشابهًا.
  2. أخبر العالم أن الكائن يريد حذفه.سيتم إعلام العالم بشكل صحيح وسيهتم بأي أشياء يجب القيام بها.شيء من هذا القبيل deleteObject(Object&) ربما.
  3. أضف shouldBeDeleted دالة لكل كائن والتي ترجع صحيحًا إذا كان الكائن يرغب في حذفه بواسطة مالكه.

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

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

الحذف الذاتي للكائن المرجعي ذو رائحة كريهة، إيمهو.فلنقارن:

// bitmap owns the data. Bitmap keeps a pointer to BitmapData, which
// is shared by multiple Bitmap instances. 
class Bitmap {
    ~Bitmap() {
        if(bmp->dec_refcount() == 0) {
            // count drops to zero => remove 
            // ref-counted object. 
            delete bmp;
        }
    }
    BitmapData *bmp;
};

class BitmapData {
    int dec_refcount();
    int inc_refcount();

};

قارن ذلك بالكائنات المعاد عدها والتي يتم حذفها ذاتيًا:

class Bitmap {
    ~Bitmap() {
        bmp->dec_refcount();
    }
    BitmapData *bmp;
};

class BitmapData {
    int dec_refcount() {
        int newCount = --count;
        if(newCount == 0) {
            delete this;
        }
        return newCount;
    }
    int inc_refcount();
};

أعتقد أن الأول هو أجمل بكثير، وأعتقد أن الكائنات المرجعية المصممة جيدًا تفعل ذلك لا افعل "احذف هذا" لأنه يزيد من الاقتران:يجب على الفصل الذي يستخدم البيانات المرجعية أن يعرف ويتذكر أن البيانات تحذف نفسها كأثر جانبي لتقليل العدد المرجعي.لاحظ كيف يمكن أن يصبح "bmp" مؤشرًا متدليًا في أداة تدمير ~Bitmap.يمكن القول أن عدم القيام بذلك "حذف هذا" هو أفضل بكثير هنا.

الإجابة على سؤال مماثل "ما فائدة حذف هذا"

وهذا هو تطبيق مرجع عد شائع الى حد كبير ولقد استعملت أن بنجاح قبل.

ولكن، ورأيت أيضا أن تحطم الطائرة. أتمنى أن نتذكر ظروف تحطم الطائرة. abelenky هو مكان واحد لقد رأيت ذلك تحطم الطائرة.

وربما كان المكان الذي أبعد فرعية Fire، لكنه يفشل في خلق المدمر الظاهري (انظر أدناه). عندما لم يكن لديك المدمر الظاهري، فإن وظيفة Update() دعوة ~Fire() بدلا من ~Flame() المدمر مناسبا.

class Fire : Object
{
public:
    virtual void Update()
    {
        if(age > burnTime)
        {
            world.Remove(this);
            delete this; //Make sure this is a virtual destructor.
        }
    }
};

class Flame: public Fire
{
private: 
    int somevariable;
};
وقد ذكر

وآخرون مشاكل مع "حذف هذا." باختصار، لأن "العالم" يدير خلق النار، فإنه ينبغي أيضا إدارة حذفها.

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

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

-

وملاحظة واحدة أكثر هو أن التعليمات البرمجية الخاصة بك كما هو مكتوب له النار وراثة خاصة من كائن، لأن هذا هو الافتراضي، على الرغم من أنه أقل شيوعا بكثير من inheiritance الجمهور. ربما يجب عليك تقديم هذا النوع من الميراث واضح، وأظن كنت تريد حقا inheritiance الجمهور.

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

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

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

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

في معظم الحالات، وأعتقد أن عمر الكائن يجب أن تدار من قبل الكائن نفسه أن يخلق ذلك. انها مجرد يجعل من السهل أن تعرف أين يحدث الدمار.

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