سؤال

أنا أعمل على لعبة Roguelike الصغيرة ، ولأي كائن/"شيء" ليس جزءًا من الخريطة يعتمد على فئة Xentity. هناك العديد من الفئات التي تعتمد عليها ، مثل Xplayer و Xitem و Xmonster.

مشكلتي هي أنني أريد تحويل مؤشر من Xentity إلى Xitem عندما أعرف أن كائنًا في العنصر. رمز العينة الذي أستخدمه لالتقاط عنصر ما هو هذا ، فهو عندما يلتقط كيان مختلف عنصرًا يقف عليه.

void XEntity::PickupItem()
{
    XEntity *Ent = MapList; // Start of a linked list

    while(true)
    {
        if(Ent == NULL) { break; }

        if(Ent->Flags & ENT_ITEM)
        {
            Ent->RemoveEntity(); // Unlink from the map's linked list

            XItem *Item = Ent // Problem is here, type-safety

            // Code to link into inventory is here

            break;
        }

        Ent = Ent->MapList;
    }
}

كانت فكرتي الأولى هي إنشاء طريقة في xentity التي تعيد نفسها كمؤشر Xitem ، لكنها تخلق تبعيات دائرية لا يمكن حلها.

أنا متعثر جدا من هذا واحد. أي مساعدة يحظى بتقدير كبير.

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

المحلول

اذا أنت أعرف أن XEntity هو actuall و XItem ثم يمكنك استخدام طاقم ثابت.

XItem* Item = static_cast<XItem *>(Ent);

ومع ذلك ، يجب عليك مراجعة تصميمك ومعرفة ما إذا كان يمكنك العمل على الكيان بطريقة تعني أنك لا تحتاج إلى معرفة النوع المشتق. إذا تمكنت من إعطاء واجهة Base Class A الغنية بما فيه الكفاية ، فقد تتمكن من القضاء على فحص نوع فحص العلم.

نصائح أخرى

الحل يحل المشكلة كما أشار الآخرون:

// dynamic_cast validates that the cast is possible. It requires RTTI 
// (runtime type identification) to work. It will return NULL if the 
// cast is not possible.
XItem* Item = dynamic_cast<XItem*>(Ent);
if(Item)
{
    // Do whatever you want with the Item.
}
else
{
    // Possibly error handling code as Ent is not an Item.
}

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

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

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

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

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

ينزل الزائر القياسي أيضًا أنه يفعل ذلك فقط من خلال آلية المكالمات الافتراضية ، والتي تكون أسرع وأكثر أمانًا في بعض الأحيان من الممثلين الصريحين. الشيء الذي لا يعجبني في هذا الزائر هو أنه إذا كنت بحاجة إلى زيارة WidgetX في برنامج Widget Higharchy ، فعليك أيضًا تنفيذ وظائف Visit () لـ Widgety و Widgetz على الرغم من أنك لا تهتم بها. مع ارتفاع و/أو عريض واسع هذا يمكن أن يكون pita. خيارات أخرى لا تتطلب هذا.

هناك أيضا "زائر الأعلى". يعرف متى تستقيل.

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

الشيء المهم الذي تحتاج إلى التفكير فيه وما تريد تجنبه ، هو كسر LSP. إذا كان لديك مجموعة كاملة من التعليمات البرمجية مع "if (widget-> type () == type1) {Outclick ...} آخر إذا (widget-> type () == type2) ..." ثم إضافة أنواع عنصر واجهة جديدة هي قضية كبيرة تؤثر على الكثير من التعليمات البرمجية بطريقة سيئة. لن تكون القطعة الجديدة الخاصة بك عنصر واجهة مستخدم لأن جميع عملائك حميمين للغاية مع التسلسل العالي الخاص بك ولا يعرفون ذلك. لا يتخلص نمط الزائر من هذه المشكلة ، لكنه يركز ، وهو أمر مهم للغاية عندما يكون لديك رائحة سيئة ، وغالبًا ما يجعل الأمر أكثر بساطة التعامل معها.

فقط يلقيها:

XItem* Item = (XItem*)Ent;

نهج أفضل ، بشكل عام ، هو:

if (XItem *Item = dynamic_cast<XItem*>(Ent)) {
    Ent->RemoveEntity();

    // Code to link into inventory is here

    break;
}
XItem * Item = dynamic_cast< XItem * >( Ent );

if ( Item )
    // do something with item

لكي يعمل ذلك ، تحتاج إلى تمكين RTTI. بحث هنا للمزيد من المعلومات.

كما تم الرد عليها ، هناك عاملان:

XItem* Item = static_cast<XItem*>(Ent);

و:

XItem* Item = dynamic_cast<XItem*>(Ent);

والثاني أبطأ ولكنه أكثر أمانًا (يتحقق إذا كان ذلك ممكنًا) وقد يعود فارغًا حتى لو Ent ليس.

أميل إلى استخدام كلاهما ملفوف بطريقة:

template <class T, class U>
T* my_cast(U* item)
{
#ifdef _NDEBUG_
  if (item) return &dynamic_cast<T&>(*item); // throw std::bad_cast
  else return 0;
#else
  return static_cast<T*>(item);
#endif
}

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

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