C ++: Как передать контейнер производных классов в функцию, ожидающую контейнер их базовых классов? - PullRequest
2 голосов
/ 04 марта 2010

HI! Кто-нибудь знает, как я могу заставить строку "chug(derlist);" в коде ниже работать?

#include <iostream>
#include <list>
using namespace std;

class Base
{
public:
    virtual void chug() { cout << "Base chug\n"; }
};

class Derived : public Base
{
public:
    virtual void chug() { cout << "Derived chug\n"; }
    void foo() { cout << "Derived foo\n"; }
};

void chug(list<Base*>& alist)
{
    for (list<Base*>::iterator i = alist.begin(), z = alist.end(); i != z; ++i)
        (*i)->chug();
}

int main() 
{
    list<Base*> baselist;
    list<Derived*> derlist;

    baselist.push_back(new Base);
    baselist.push_back(new Base);
    derlist.push_back(new Derived);
    derlist.push_back(new Derived);

    chug(baselist);
    // chug(derlist);  // How do I make this work?

    return 0;
}

Причина, по которой мне это нужно, заключается в основном в том, что у меня есть контейнер очень сложных объектов, которые мне нужно передать определенным функциям, которые заботятся только об одной или двух виртуальных функциях в этих сложных объектах.

Я знаю, что короткий ответ: «Вы не можете», я действительно ищу какие-то хитрости / идиомы, которые люди используют, чтобы обойти эту проблему.

Заранее спасибо.

Ответы [ 4 ]

5 голосов
/ 04 марта 2010

Ваш вопрос странный; субъект спрашивает: «Как мне положить вещи в контейнер, не теряя при этом полиморфизма» - но это напрашивается на вопрос; предметы в контейнерах не теряют полиморфизм. У вас просто контейнер базового типа, и все работает.

Из вашего примера видно, что вы спрашиваете: «Как преобразовать контейнер дочерних указателей в контейнер базовых указателей?» - и ответ на этот вопрос, вы не можете. дочерние указатели конвертируются в базовые указатели, контейнеры дочерних указателей - нет. Они не связаны между собой. Хотя, обратите внимание, что shared_ptr можно преобразовать в shared_ptr, но только потому, что у них есть дополнительное волшебство, чтобы заставить это работать. Контейнеры не имеют такой магии.

Одним из ответов было бы сделать chug функцией шаблона (отказ от ответственности: я не на компьютере с компилятором, поэтому я не пытался скомпилировать это):

template<typename C, typename T>
void chug(const C<T>& container)
{
    typedef typename C<T>::iterator iter;
    for(iter i = container.begin(); i < container.end(); ++i)
    {
        (*i)->chug();
    }
}

Тогда chug может взять любой контейнер любого типа, если он является контейнером указателей и имеет метод chug.

1 голос
/ 04 марта 2010

Либо сохраняйте их по указателю (популярный вариант: boost :: shared_ptr), либо используйте Boost ptr_containers, которые хранят указатели внутри, но предлагают хороший API снаружи (и, конечно, полностью автоматизированное удаление).

#include <boost/ptr_container/ptr_vector.hpp>

boost::ptr_vector<FooBase> foos;
foos.push_back(new FooDerived(...));
foos[0].memberFunc();

Полиморфное преобразование контейнеров просто невозможно, поэтому всегда передавайте ptr_vector & и downcast в самой функции.

0 голосов
/ 04 марта 2010

Почему бы не сделать chug функцией на основе шаблона, чтобы в ней могли быть экземпляры для базовых и производных типов?

Или, для лучшего решения вы можете использовать std::for_each.

0 голосов
/ 04 марта 2010

Возможно, вы можете сделать chug функцией шаблона, которая преобразует параметр шаблона в тип Base*, а затем вызывает chug для этого.

...