ما هي الطريقة الأكثر أمانًا لتخزين كائنات الفئات المستمدة من واجهة مشتركة في حاوية مشتركة؟

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

سؤال

أرغب في إدارة مجموعة من كائنات الفئات المستمدة من فئة واجهة مشتركة في حاوية مشتركة.

لتوضيح المشكلة ، دعنا نقول أنني أبني لعبة تحتوي على ممثلين مختلفين. دعنا نسمي الواجهة IActor واشتقاق Enemy و Civilian منه.

الآن ، تتمثل الفكرة في أن تتمكن حلقة اللعبة الرئيسية من القيام بذلك:

// somewhere during init
std::vector<IActor> ActorList;
Enemy EvilGuy; 
Civilian CoolGuy;
ActorList.push_back(EvilGuy);
ActorList.push_back(CoolGuy);

و

// main loop
while(!done) {
    BOOST_FOREACH(IActor CurrentActor, ActorList) {
        CurrentActor.Update();
        CurrentActor.Draw();
    }
}

... او هناك شيء ما على طول هذه الخطوط. من الواضح أن هذا المثال لن ينجح ، لكن هذا هو السبب في أنني أسأل هنا.

أود أن أعرف: ما هي أفضل طريقة وأكثرها أمانًا وأعلى مستوى لإدارة تلك الكائنات في حاوية شائعة غير متجانسة؟ أعلم عن مجموعة متنوعة من الأساليب (Boost :: any ، void*، فئة Handler مع Boost :: shared_ptr ، boost.pointer container ، dynamic_cast) ولكن لا يمكنني تحديد أي وسيلة للذهاب إلى هنا.

كما أود أن أؤكد أنني أريد الابتعاد إلى أقصى حد ممكن من إدارة الذاكرة اليدوية أو المؤشرات المتداخلة.

مساعدة كبيرة في تقدير :).

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

المحلول

كما خمنت أنك بحاجة إلى تخزين الكائنات كمؤشرات.
أفضل استخدام حاويات مؤشر Boost (بدلاً من حاوية عادية من المؤشرات الذكية).

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

عيب المؤشرات الذكية هو أنك تشارك الملكية.
هذا ليس ما تريده حقًا. تريد أن تكون الملكية في مكان واحد (في هذه الحالة الحاوية).

boost::ptr_vector<IActor> ActorList; 
ActorList.push_back(new Enemy()); 
ActorList.push_back(new Civilian());

و

std::for_each(ActorList.begin(), 
              ActorList.end(),
              std::mem_fun_ref(&IActor::updateDraw));

نصائح أخرى

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

  • حدد فئة أساسية (التي تفعله بالفعل) مع وظائف افتراضية يتم تجاوزها بواسطة الفئات المشتقة Enemy و Civilian في حالتك.
  • تحتاج إلى اختيار حاوية مناسبة مع تخزين الكائن الخاص بك. لقد أخذت ملف std::vector<IActor> وهو ليس خيارًا جيدًا بسبب
    • أولاً عندما تقوم بإضافة كائنات إلى المتجه ، فإنه يؤدي إلى تقطيع الكائنات. هذا يعني أن فقط IActor جزء من Enemy أو Civilian يتم تخزينه بدلاً من الكائن بأكمله.
    • ثانياً ، تحتاج إلى استدعاء الوظائف اعتمادًا على نوع الكائن (virtual functions) ، والتي لا يمكن أن تحدث إلا إذا كنت تستخدم المؤشرات.

يشير كلا السبب أعلاه إلى حقيقة أنك تحتاج إلى استخدام حاوية يمكن أن تحتوي على مؤشرات ، شيء مثل std::vector<IActor*> . لكن الخيار الأفضل هو الاستخدام container of smart pointers مما سيوفر لك من صداع إدارة الذاكرة. يمكنك استخدام أي من المؤشرات الذكية اعتمادًا على حاجتك (ولكن ليس auto_ptr)

هذا ما سيبدوه رمزك

// somewhere during init
std::vector<some_smart_ptr<IActor> > ActorList;
ActorList.push_back(some_smart_ptr(new Enemy()));
ActorList.push_back(some_smart_ptr(new Civilian()));

و

// main loop
while(!done) 
{
    BOOST_FOREACH(some_smart_ptr<IActor> CurrentActor, ActorList) 
    {
        CurrentActor->Update();
        CurrentActor->Draw();
     }
}

الذي يشبه إلى حد كبير رمزك الأصلي باستثناء جزء المؤشرات الذكية

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

إذا كنت تريد أن تملك الحاوية عناصرها حصريًا ، فاستخدم حاوية مؤشر Boost: فهي مصممة لهذه المهمة. خلاف ذلك ، استخدم حاوية من shared_ptr<IActor> (وبالطبع استخدمها بشكل صحيح ، مما يعني أن كل من يحتاج إلى مشاركة استخدامات الملكية shared_ptr).

في كلتا الحالتين ، تأكد من أن المدمر IActor افتراضي.

void* يتطلب منك القيام بإدارة الذاكرة اليدوية ، لذلك هذا خارج. Boost.Ine يكون مبالغة عندما تكون الأنواع مرتبطة بالميراث - تعدد الأشكال القياسي يقوم بالمهمة.

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

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