0

Is it posible to have a std::list of a base classes, put into it subclasses of few types, and access the children class members iterating?

freesoul
  • 528
  • 5
  • 14
  • You should be able to cast it to the correct type, so sure. What have you tried and why did that fail? – default Aug 15 '13 at 14:20

3 Answers3

3

If your element types are polymorphic, then yes.

The only catch is that you need to store pointers to those polymorphic objects, because containers own their elements and if you were to try to add a Base into your list, it would be copied and sliced, losing all the context of its more derived nature. If it's an abstract class (as it should be), then the code won't even compile.

I advise against raw pointers, so this is what we get:

#include <list>
#include <memory>
#include <iostream>

struct Base
{
   virtual ~Base() {}
   virtual void foo() = 0;
};

struct Derived1 : Base
{
   void foo() { std::cout << "Lol!\n"; }
};

struct Derived2 : Base
{
   void foo() { std::cout << "Haha!\n"; }
};

int main()
{
   typedef std::list<std::unique_ptr<Base>> list_type;

   list_type l;
   l.emplace_back(new Derived1());
   l.emplace_back(new Derived2());
   l.emplace_back(new Derived2());
   l.emplace_back(new Derived1());

   for (auto& x : l)
      x->foo();      // <-- this is a VIRTUAL function call!
}

Output:

Lol!
Haha!
Haha!
Lol!

Yeah, it's a little verbose, but you can tidy that up with wrapper functions and whatnot.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
1

Yes, it is possible - but make sure to store pointers instead of values to avoid the slicing problem:

#include <iostream>
#include <list>

struct Base {
  virtual ~Base() {}
  virtual void f() = 0;
};

struct DerivedA : Base {
  virtual void f() { std::cout << "DerivedA::f\n"; }
};

struct DerivedB : Base {
  virtual void f() { std::cout << "DerivedB::f\n"; }
};

int main()
{
  std::list<Base *> l;
  l.push_back( new DerivedA );
  l.push_back( new DerivedB );

  for ( std::list<Base *>::const_iterator it = l.begin(); it != l.end(); ++it ) {
    (*it)->f();
  }
}

This prints

DerivedA::f
DerivedB::f

Instead of storing raw pointers it would be better to use some sort of smart pointer which takes care of calling delete as appropriate. C++11 features various smart pointers, make sure to not use std::auto_ptr though!

Community
  • 1
  • 1
Frerich Raabe
  • 90,689
  • 19
  • 115
  • 207
0

You can't put Derived to std::list<Base> because of slicing, But you may do it with pointers (or better smart pointers).

std::list<Base*> l;
l.push_back(new Derived); // don't forget to delete it!!

you can call (*l.begin())->method_that_present_in_base(). If this method is virtual, method of the derived will be called.

Example:

#include <iostream>
#include <list>

struct Animal {
    virtual void say() {
        std::cout <<"I'm generic animal\n";
    }

    virtual ~Animal(){}
};

struct Cat : public Animal {
    virtual void say() {
        std::cout << "Meow\n";
    }
    virtual ~Cat(){}
};

int main() {
    std::list<Animal*> l;
    l.push_back(new Animal);
    l.push_back(new Cat);
    for(Animal* ptr: l) {
        ptr->say();
    }
    for(Animal* ptr: l) {
        delete ptr; //not needed if you use smart pointers.
    }
}

Output

I'm generic animal
Meow

Community
  • 1
  • 1
RiaD
  • 46,822
  • 11
  • 79
  • 123