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?

- 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 Answers
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!
}
Lol!
Haha!
Haha!
Lol!
Yeah, it's a little verbose, but you can tidy that up with wrapper functions and whatnot.

- 378,754
- 76
- 643
- 1,055
-
You could reduce the verbosity with `l.emplace_back(new Derived1)` – Mike Seymour Aug 15 '13 at 14:45
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!

- 1
- 1

- 90,689
- 19
- 115
- 207
-
Bah, you beat me to it because I got hung up on getting the C++11 mess right. – Lightness Races in Orbit Aug 15 '13 at 14:33
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.
}
}
I'm generic animal
Meow