سؤال

لدي حاوية من المؤشرات الذكية للكائنات القابلة للتغيير.لا بد لي من كتابة اثنين for_each حلقات، واحدة للوصول إلى الكائنات كبيانات للقراءة فقط والأخرى للبيانات القابلة للتغيير.المترجم يقول لي ذلك std::vector< boost::shared_ptr<Object> > ليست هي نفسها std::vector< boost::shared_ptr<const Object> >, ، لاحظ ال const.

هنا هو رمز المثال الخاص بي:

#include <vector>
#include "boost/shared_ptr.hpp"
#include <iterator>

class Field_Interface
{ ; };
typedef boost::shared_ptr<Field_Interface> Ptr_Field_Interface;
typedef boost::shared_ptr<const Field_Interface> Ptr_Const_Field_Interface;

struct Field_Iterator
  : std::input_iterator<std::forward_iterator_tag, Ptr_Field_Interface>
{
  // forward iterator methods & operators...
};

struct Const_Field_Iterator
  : std::input_iterator<std::forward_iterator_tag, Ptr_Const_Field_Interface>
{
  // forward iterator methods & operators...
};

struct Field_Functor
{
  virtual void operator()(const Ptr_Field_Interface&) = 0;
  virtual void operator()(const Ptr_Const_Field_Interface&) = 0;
};

class Record;
typedef boost::shared_ptr<Record> Ptr_Record;
typedef boost::shared_ptr<const Record> Ptr_Const_Record;

class Record_Base
{
  protected:
    virtual Field_Iterator beginning_field(void) = 0;
    virtual Field_Iterator ending_field(void) = 0;
    virtual Const_Field_Iterator const_beginning_field(void) = 0;
    virtual Const_Field_Iterator const_ending_field(void) = 0;

    void for_each(Field_Functor * p_functor)
    {
       Field_Iterator iter_begin(beginning_field());
       Field_Iterator iter_end(ending_field());
       for (; iter_begin != iter_end; ++ iter_begin)
       {
         (*p_functor)(*iter_begin);
       }
     }
};

class Record_Derived
{
public:
   typedef std::vector<Ptr_Field_Interface> Field_Container;
   typedef std::vector<Ptr_Record>          Record_Container;
private:
   Field_Container m_fields;
   Record_Container m_subrecords;
};

بالنظر إلى كل التفاصيل المذكورة أعلاه، كيف يمكنني تنفيذ الأساليب التجريدية البحتة لـ Record_Base في Record_Derived?

لقد حاولت:

  • عودة m_fields.begin(), ، أيّ
    إرجاع أخطاء التحويل (لا يمكن تحويلها std::vector<...> to Field_Iterator)
  • عودة &m_fields[0], ، وهو أمر خطير ، لأنه يفترض أشياء عن الداخلية std::vector.

راجع للشغل، وأنا لا تستخدم std::for_each لأنه لا بد لي من التكرار على حاوية من الحقول و حاوية من السجلات الفرعية.

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

المحلول

ما تفعله يشبه كلا من مركب و زائر أنماط.يتشابك هذان النمطان جيدًا معًا، لذا يبدو أنك على المسار الصحيح.

لتنفيذ النمط المركب، قم بتعيين الأدوار التالية (راجع مخطط UML للنمط المركب):

  • ورقة -> Field
  • مركب -> Record
  • المكون -> فئة أساسية مجردة من Field و Record (لا أستطيع التفكير في اسم جيد)

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

لتنفيذ نمط الزائر، الزائد operator() في فئات functor الخاصة بك لكل نوع فرعي من المكونات (الحقل والتسجيل).

أنصحك بالحصول على نسخة من أنماط التصميم كتاب من تأليف "عصابة الأربعة"، والذي يشرح هذه المفاهيم بشكل أفضل ويتناول تفاصيل أكثر بكثير مما أستطيع.

فيما يلي بعض نماذج التعليمات البرمجية لإثارة شهيتك:

#include <iostream>
#include <vector>
#include "boost/shared_ptr.hpp"
#include "boost/foreach.hpp"

class Field;
class Record;

struct Visitor
{
    virtual void operator()(Field& field) = 0;
    virtual void operator()(Record& field) = 0;
};

class Component
{
public:
    virtual bool isLeaf() const {return true;}
    virtual void accept(Visitor& visitor) = 0;
};
typedef boost::shared_ptr<Component> ComponentPtr;

class Field : public Component
{
public:
    explicit Field(int value) : value_(value) {}
    void accept(Visitor& visitor) {visitor(*this);}
    int value() const {return value_;}

private:
    int value_;
};

class Record : public Component
{
public:
    typedef std::vector<ComponentPtr> Children;
    Record(int id) : id_(id) {}
    int id() const {return id_;}
    Children& children() {return children_;}
    const Children& children() const {return children_;}
    bool isLeaf() const {return false;}
    void accept(Visitor& visitor)
    {
        visitor(*this);
        BOOST_FOREACH(ComponentPtr& child, children_)
        {
            child->accept(visitor);
        }
    }

private:
    int id_;
    Children children_;
};
typedef boost::shared_ptr<Record> RecordPtr;

struct OStreamVisitor : public Visitor
{
    OStreamVisitor(std::ostream& out) : out_(out) {}
    void operator()(Field& field) {out_ << "field(" << field.value() << ") ";}
    void operator()(Record& rec) {out_ << "rec(" << rec.id() << ") ";}
    std::ostream& out_;
};

int main()
{
    RecordPtr rec(new Record(2));
        rec->children().push_back(ComponentPtr(new Field(201)));
        rec->children().push_back(ComponentPtr(new Field(202)));
    RecordPtr root(new Record(1));
        root->children().push_back(ComponentPtr(new Field(101)));
        root->children().push_back(rec);

    OStreamVisitor visitor(std::cout);
    root->accept(visitor);
}

في السجل، قد ترغب في توفير طرق لمعالجة/الوصول إلى الأطفال، بدلاً من إرجاع مرجع إلى المتجه الفرعي الأساسي.

نصائح أخرى

أود أن أقترح عدم كتابة مكرر خاص بك عند استخدام نوع حاوية شائع.إن كتابة مكرر خاص أمر منطقي عندما تكتب حاوية خاصة بك.ومع ذلك، عندما تخطط لكتابة مكرر مخصص، قم بإلقاء نظرة على Boost.Iterator طَرد.

إذا كنت تريد الاختباء std::vector ومكرراتها من المستخدم، فستحتاج إلى توفير مكررات متعددة الأشكال لتتوافق مع متعددة الأشكال الخاصة بك RecordBase حاوية.الدفع any_iterator من مكتبة Adobe ASL. هؤلاء الروابط قد يكون من المفيد أيضا.

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

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