Проблема C ++ с полиморфизмом и векторами указателей

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

Вопрос

Рассмотрим следующий пример кода:

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 и бросить их на указания на Barв клиентском коде, но это не так, поскольку клиент не должен знать о Fooсуществование.

Я также мог бы определить get() участник, который извлекает объекты из d_foos И бросает их, но это кажется довольно неуклюжим. Предпочтительно, я хотел бы просто вернуть d_foos как vector<shared_ptr<Bar> > &, но я не могу этого сделать.

Также может случиться так, что мой дизайн просто неправильный. Это казалось наиболее естественным решением, хотя, как Bar является специализацией Foo а также BarCollection является специализацией FooCollection И они делятся функциональностью.

Не могли бы вы предложить хорошие решения для реализации getBars в BarCollection Или лучшие альтернативы дизайна?

Редактировать:

Оказывается, мой дизайн действительно был плохим. Образец не является фоколлекцией, несмотря на то, что требует всей функциональности 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);
     }
 };

Обратите внимание, что этот класс будет иметь ту же проблему, что и итератор, операция на векторе может закончить этот класс.

Еще одна мысль

Вы можете не осознавать этого, но может быть совершенно хорошо, чтобы просто вернуть вектор Foo. Пользователь Bar уже должен иметь полное знание Foo, поскольку, включив Bar.h, он должен получить Foo.h через Bar.H. Причина в том, что для бара унаследовать от Foo, он должен иметь полное знание класса через 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 Но интерпретируется как требуемый тип ... на самом деле не то, что я люблю видеть в коде.

Примечание:
Там может быть способ сделать лучше, чем это с помощью boost.fusion Transform_View Класс, но я не мог понять это.

В частности, использование transform_view я могу получить shared_ptr<Bar> Но я не могу получить shared_ptr<Bar>& Когда я преодолею свой итератор, что раздражает, учитывая, что единственное использование возврата ссылки на базовую vector (и не const_reference) фактически изменить структуру vector и объекты, которые он содержит.

Заметка 2:
Пожалуйста, рассмотрите рефакторинг. Там были отличные предложения.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top