2012-06-07 3 views
2

У меня есть 3 разных класса (A, B, C), каждый из которых имеет shared_ptr для общего объекта (объект X). Я хочу сделать, чтобы предоставить объекту X информацию о том, кто указывает на него. Например, если бы не было объектов A, указывающих на него, нет необходимости вычислять данные, которые используются только классами A при его обновлении. И объект X может содержать вектор указателей на объекты C, которые указывают на него, чтобы он мог вызывать функцию на них, когда некоторые данные, относящиеся к объектам C, были обновлены (но объекты C по-прежнему указывают на него, чтобы запрашивать другие данные из него).Объект, который знает, кто указывает на него

Что я собираюсь сделать, это создать класс-оболочку для shared_ptr для каждого класса, который будет вызывать указатели на функции на X всякий раз, когда оболочка будет построена/назначена/уничтожена (все те же случаи, которые увеличивают/уменьшают ссылочную считать на X). Вот идея в коде:

class A 
{ 
public: 
    A() : m_ptr(&X::IncrementACount, &X::DecrementACount) { } 

    void SetX(const std::shared_ptr<X> &ptr) { m_ptr = ptr; } 
private: 
    shared_ptr_wrapper0<X> m_ptr; 
}; 

class B 
{ 
public: 
    B() : m_ptr(&X::IncrementBCount, &X::DecrementBCount) { } 

    void SetX(const std::shared_ptr<X> &ptr) { m_ptr = ptr; } 
private: 
    shared_ptr_wrapper0<X> m_ptr; 
}; 

class C 
{ 
public: 
    C() : m_ptr(this, &X::StoreCPointer, &X::RemoveCPointer) { } 

    void SetX(const std::shared_ptr<X> &ptr) { m_ptr = ptr; } 
private: 
    shared_ptr_wrapper1<X, const C*> m_ptr; 
}; 

А и В имеют обернутый интеллектуальный указатель с/декремента функции Increment уточнил, что не принимают никаких аргументов. Когда обернутый смарт-указатель получает set/cleared/etc, эти функции будут вызываться на содержащем его объекте X и увеличивать/уменьшать счетчики A/B.

C имеет завернутый смарт-указатель с одним аргументом (типа const C *). Когда он получает set/cleared/etc, функции на нем будут вызываться с данными, которые были сохранены на нем конструктором C (этим указателем), и будут добавлять или удалять его из вектора на объекте X.

So мой главный вопрос - это хорошая идея? Есть ли лучший способ достичь этой идеи, чтобы отслеживать, кто указывает на объект? Есть ли конкретное имя для такого типа идеи (я не могу себе представить, что я первым попробую что-то подобное, но я не нашел ничего, когда искал его, поэтому, возможно, я просто не знаю правильного имени для искать ...)

спасибо.

+1

Это невероятно глупо, запутанно и что он должен решить? –

+0

Если вы просто отслеживаете, если объекты _some_ типа 'A' ​​или' B' имеют ссылки на объекты типа 'C', я должен думать, что вы можете хранить простой _count_ объектов' A' или 'B' которые имеют ссылки в объекте 'C', а _ только_' C'-объект. Почему «A» и «B» должны отслеживать это от имени «C»? – sarnold

+0

(Кстати, мне нравится ядро ​​_idea_ вычисления только того, что необходимо для клиентов объектов, эта конкретная реализация чувствует, что она отслеживает слишком много данных в неправильных местах, но, возможно, я неправильно прочитал ее.) – sarnold

ответ

4

Простым подходом будет шаблон наблюдателя, известный как публикация/подписчик. См. http://en.wikipedia.org/wiki/Observer_pattern. Ваш объект X является издателем и может предлагать 3 разных интерфейса. Ваши объекты A, B и C являются подписчиками, и они регистрируются, чтобы получать уведомления об изменениях. Каждый Наблюдатель несет ответственность за добавление и удаление из списков подписчиков X. Когда что-то меняется в X, каждый из соответствующих подписных листов уведомляется через свой интерфейс.

+0

Пример, который я дал для класса C, должен был быть примером шаблона наблюдателя. В shared_ptr_wrapper1 он использует подписчиков на объект X, когда он устанавливается, и отменяет подписку при удалении указателя/C разрушается. Правильно? Я не уверен, как шаблон наблюдателя применим к классу A/B. Идея там как то, что у меня уже есть refcount, которая подсказывает мне, сколько объектов указывает на объект X, я просто хотел расширить это, чтобы добавить два дополнительных подсчета ref, которые отслеживают только то, сколько объектов A/B указывают на него. Будет ли более простой способ достичь этой идеи вторичного рефлексии? –

0

Ваша проблема в том, что единственным объектом, действительно указывающим на ваш объект A, является умный указатель. Поэтому, даже если вы можете уведомить свой объект, когда умный указатель скопирован (поиск интрузивного указателя), нет никакого способа узнать , который выполняет копирование умного poitner, что вы действительно хотите знать.

Вам нужно что-то вроде паба/подмодели.

Ниже приведен псевдокод, чтобы дать представление о том, где мы хотим отслеживать двух разных типов подписчиков.

class A 
{ 
private: 
    std::vector<B&> subscriberB; 
    std::vector<C&> subscriberC; 

public: 
    void Subscribe(const B& subscriber) { subscriberB.push_back(subscriber); } 
    void Subscribe(const C& subscriber) { subscriberC.push_back(subscriber); } 

    // An example of a function where the subscribing objects are notified. 
    void SomeFunction() 
    { 
     // for each subscriber in subscriberB 
     subscriber.SomeFunction(); 

     // for each subscriber in subscriberC 
     subscriber.SomeOtherFunction(); 
    } 

}; 

Приведенное выше содержит ссылки, предполагая, что абоненты ожидают объект, на который они подписываются. Если нет, вы должны поместить интеллектуальные указатели в векторы. Кроме того, вы можете запретить один и тот же объект подписываться дважды или предоставить метод отмены подписки.

Если вы хотите, чтобы «силы» объекты типа B или C, чтобы быть связаны с А, вы могли бы сделать их конструктор что-то вроде:

// B ctor 
B::B(shared_ptr<A> aPtr) 
{ 
    myPointerToA = aPtr; 
    myPointerToA ->subscribe(*this); 
} 

еще лучше было бы использовать фабричный метод, который создает B , а затем перехватывает его до A: это позволяет избежать разыменования this в конструкторе, который немного пахнет.

Более типичный подход заключается в том, чтобы иметь один тип подписчика InterfaceSubscribeToA, который будет реализован любым классом, который хочет подписаться на A. Это полностью зависит от того, чего вы пытаетесь достичь, я просто упомянул об этом, если вы оказываетесь слишком сложными в вашей конкретной проблеме!

0

Вы можете использовать список накладных. Сделайте class Base и сделайте A, B и C наследуйте этот класс. Кроме того, лучшая техника заключается не в том, чтобы отличать C от A и B, но относиться ко всем наблюдателям таким же образом (что, если вы добавите D со своим особым действием, отличным от C?). Класс Base идеален для этого.

class Base 
{ 
public: 
    virtual void notifyXObjectChanged(/* data you want to pass*/) {} 
    void setX(X* obj) 
    { 
     if(obj != object) 
     { 
      if(object) 
       object->removeFromList(this); 
      object = obj; 
      if(object) 
       object->addToList(this); 
     } 
    } 

private: 
    friend class X; 
    X* object; 
    Base* next; 
    Base* previous; 
}; 

И, в X:

class X 
{ 
    // ... 
public: 
    void addToList(Base* obj) 
    { 
     if(first == 0 && last == 0) 
     { 
      // The list is empty 
      first = last = obj; 
      obj->previous = obj->next = 0; 
     } 
     else 
     { 
      // Add the object at the end of the list 
      last->next = obj; 
      obj->previous = last; 
      obj->next = 0; 
      last = obj; 
     } 
    } 

    void removeFromList 
    { 
     // Code for removing an element in a list 
    } 

    void notify() 
    { 
     // iterate on the list 
    } 

private: 
    Base* first; 
    Base* last; 
    // ... 
}; 

Преимущество:

  • очень небольшие накладные расходы памяти
  • очень быстро

Надеется, что это помогает

Смежные вопросы