سؤال

النظر في رمز المثال التالي:

class Foo
{
};

class Bar : public Foo
{
};

class FooCollection
{
protected:
    vector<shared_ptr<Foo> > d_foos;
};

class BarCollection : public FooCollection
{
public:
    vector<shared_ptr<Bar> > &getBars()
    {
        // return d_foos won't do here...
    }
};

لدي مشكلة مثل هذه في مشروعي الحالي. يستخدم رمز العميل BarCollection, التي تخزن المؤشرات ل Bars في d_foos الذي أعلن في FooCollection. أود الآن فضح مجموعة المؤشرات إلى الحانات إلى رمز العميل. يمكنني فقط إعطاء رمز العميل الوصول إلى متجه المؤشرات FooS ويلقي هذه على المؤشرات BarS في رمز العميل ، ولكن هذا يبدو خطأ لأن العميل لا يجب أن يعرف عنه Fooوجود.

يمكنني أيضا تحديد أ get() العضو الذي يسترجع الأشياء من d_foos ويلقي بهم ، ولكن هذا يشعر خرقاء للغاية. على نحو مفضل ، أود فقط إعادة d_foos ك vector<shared_ptr<Bar> > &, ، لكن لا يمكنني القيام بذلك.

قد يكون أيضًا أن تصميمي هو مجرد خطأ. يبدو أن الحل الأكثر طبيعية رغم ذلك Bar هو تخصص Foo و BarCollection هو تخصص FooCollection وهم يشاركون الوظيفة.

هل يمكن أن تقترح حلولًا لطيفة للتنفيذ getBars في BarCollection أو أفضل بدائل تصميم؟

يحرر:

تبين أن تصميمي كان سيئًا بالفعل. Barcollection ليس بمثابة Fooocollection ، على الرغم من طلب جميع وظائف Foocollection. إن حلي الحالي استنادًا إلى الإجابات أدناه - وهو أكثر نظافة - هو الآن:

class Foo
{
};

class Bar : public Foo
{
};

template<class T>
class Collection
{
    vector<shared_ptr<T> > d_items;
};

typedef Collection<Foo> FooCollection;

class BarCollection : public Collection<Bar>
{
    // Additional stuff here.
};

شكرا لجميع الاقتراحات الممتازة والأمثلة!

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

المحلول

template<class T>
class MyContainer {
  vector<shared_ptr<T> > d_foos;
public:
  vector<shared_ptr<T> > & getVector();
};

class FooCollection : public MyContainer<Foo> {
};

class BarCollection : public MyContainer<Bar> {
};

نصائح أخرى

أود أن أقترح تعريض التكرار من فصول الحاويات الخاصة بك ، بدلاً من حاوية الأعضاء. بهذه الطريقة لن يهم نوع الحاوية.

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

class<Foo>

و

class<Bar>

قد يكون أحد الاقتراحات هو إنشاء محول مشتق FOO الذي يلقي بالفئة الصحيحة:

 template <class derived, class base>
 class DowncastContainerAdapter
 {
 private:
     std::vector< boost::shared_ptr<base> >::iterator curr;
     std::vector< boost::shared_ptr<base> >::const_iterator end;
 public:
     DowncastContainerAdapter(/*setup curr & end iterators*/)
     {
         // assert derived actually is derived from base
     }

     boost::shared_ptr<derived> GetNext()
     {
         // increment iterator
         ++curr;
         return dynamic_cast<base>(*curr);
     }

     bool IsEnd()
     {
         return (curr == end);
     }
 };

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

فكر آخر

قد لا تدرك ذلك ، ولكن قد يكون من الجيد تمامًا إعادة متجه فو. يجب أن يكون لدى مستخدم Bar بالفعل معرفة كاملة بـ FOO ، لأنه من خلال تضمين BAR.H يجب أن يحصلوا على foo.h من خلال BAR.H. والسبب هو أنه لكي يرث بار من فو ، يجب أن يكون لديه معرفة كاملة بالصف عبر Foo.H. أود أن أقترح بدلاً من استخدام الحل أعلاه ، إذا كان ذلك ممكنًا ، اجعل FOO (أو فئة فائقة من FOO) فئة واجهة وتمر حول متجهات المؤشرات إلى فئة الواجهة هذه. هذا نمط شائع جدًا ولن يرفع الحواجب التي توصلت إليها هذا الحل المتزعزع الذي توصلت إليه :). ثم مرة أخرى قد يكون لديك أسبابك. حظا سعيدا في كلتا الحالتين.

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

لا يمكنك استبدال هذا بمجموعة معبد على إما foo / bar؟ ، شيء من هذا القبيل

class Collection<T> {
protected:
    vector<shared_ptr<T> > d_foos;
};

typedef Collection<Foo> FooCollection;
typedef Collection<Bar> BarCollection;

هل لديك حاجة خاصة إلى BarCollection مستمدة من FooCollection؟ لأنه عموما أ BarCollection ليس أ FooCollection, ، عادة ما يكون الكثير من الأشياء التي يمكن القيام بها مع أ FooCollection لا ينبغي القيام به مع أ BarCollection. فمثلا:

BarCollection *bc = new BarCollection();
FooCollection *fc = bc; // They are derived from each other to be able to do this
fc->addFoo(Foo());      // Of course we can add a Foo to a FooCollection

الآن أضفنا أ Foo الاعتراض على ما يفترض أن يكون BarCollection. إذا BarCollection يحاول الوصول إلى هذا العنصر المضافة حديثًا ويتوقع أن يكون Bar, ، ستحدث جميع أنواع الأشياء القبيحة.

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

بادئ ذي بدء ، لنتحدث عنه shared_ptr. هل تعرف عن: boost::detail::dynamic_cast_tag ?

shared_ptr<Foo> fooPtr(new Bar());
shared_ptr<Bar> barPtr(fooPtr, boost::detail::dynamic_cast_tag());

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

العودة إلى سؤالك: الرمز السيئ.

BarCollection ليس FooCollection, ، كما ذكرنا ، فأنت في ورطة لأنه يمكنك تقديم عناصر أخرى في متجه المؤشرات التي Bar تلك.

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

لا يمكنك تمرير مرجع ، ولكن يمكنك تمرير ملف View.

أساسا ، أ View هو كائن جديد بمثابة أ Proxy إلى القديم. من السهل نسبيًا استخدام Boost.iterators من المثال.

class VectorView
{
  typedef std::vector< std::shared_ptr<Foo> > base_type;

public:
  typedef Bar value_type;
  // all the cluttering

  class iterator: boost::iterator::iterator_adaptor<
    iterator,
    typename base_type::iterator,
    std::shared_ptr<Bar>
  >
  {
    typename iterator_adaptor::reference dereference() const
    {
      // If you have a heart weakness, you'd better stop here...
      return reinterpret_cast< std::shared_ptr<Bar> >(this->base_reference());
    }
  };

  // idem for const_iterator

  // On to the method forwarding
  iterator begin() { return iterator(m_reference.begin()); }

private:
  base_type& m_reference;
}; // class VectorView

المشكلة الحقيقية هنا هي بالطبع reference قليل. الحصول على NEW shared_ptr الكائن سهل ، والسماح لأداء أ dynamic_cast كما هو مطلوب. الحصول على reference إلى ORIGINAL shared_ptr لكن تم تفسيره على أنه النوع المطلوب ... ليس ما أحب أن أراه في الكود.

ملحوظة:
قد يكون هناك طريقة للقيام بعمل أفضل من استخدام التعزيز. الانصهار transform_view الفصل ، لكنني لم أستطع معرفة ذلك.

على وجه الخصوص ، باستخدام transform_view أستطيع الحصول على shared_ptr<Bar> لكن لا يمكنني الحصول على ملف shared_ptr<Bar>& عندما أخطررني ، وهو أمر مزعج بالنظر إلى أن الاستخدام الوحيد لإرجاع الإشارة إلى الأساس vector (وليس أ const_reference) هو في الواقع تعديل بنية vector والكائنات التي تحتوي عليها.

ملاحظة 2:
يرجى النظر في إعادة البناء. كانت هناك اقتراحات ممتازة هناك.

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