In the below code I am simply trying to experiment with a Heterogeneous std::list where I have stored three Derived class object in a list of type Base*. When retrieving data fr
Your cast (Derived1*)*itr
is equivalent to a static_cast
which states:
If the prvalue of type “pointer to cv1
B
” points to aB
that is actually a subobject of an object of typeD
, the resulting pointer points to the enclosing object of typeD
. Otherwise, the result of the cast is undefined.
So you have undefined behaviour because you are casting pointers that point at Derived2
and Derived3
objects to a Derived1*
.
The proper way to achieve this is to have std::string s;
defined in Base
and give it a constructor that takes a std::string
argument. Then you can make the constructors of Derived1
to Derived3
call this constructor with the appropriate string argument.
If you add any extra members to one of the Derived
classes, you won't be able to access them through the polymorphic Base*
. However, trying to do that is a sign of bad design. You should only be treating the objects that a Base*
points at as though they were Base
objects. That is, Base
describes the interface for those objects.
If you really need to do something that is derived class specific to an object pointed to a Base*
, you can use a dynamic_cast
:
if (Derived1* p = dynamic_cast<Derived1*>(*itr)) {
// This condition will only be true if the object pointed to by `*itr` really is a Derived1
}
#include <list>
#include <vector>
#include <iostream>
#include <string.h>
class Base;
typedef std::list<Base*> any_list;
class Base{
public:
virtual ~Base(){}
virtual std::string getData()=0;
};
class Derived1 : public Base
{
public:
std::string s;
Derived1():s("D1"){}
std::string getData()
{
return s;
}
};
class Derived2 : public Base
{
public:
std::string s;
Derived2():s("D2"){}
std::string getData()
{
return s;
}
};
class Derived3 : public Base
{
public:
std::string s;
Derived3():s("D3"){}
std::string getData()
{
return s;
}
};
int main(int argc, char **argv) {
any_list l;
l.push_back(new Derived1);
l.push_back(new Derived2);
l.push_back(new Derived3);
for(any_list::iterator itr=l.begin();itr!=l.end();++itr)
std::cout<<((Base*)*itr)->getData()<<std::endl;
}
Thanks for the help. Confusion is clear and the problem is solved. By d way what is -
typedef std::list< std::unique_ptr > any_list;
I didn't find anything called unique_ptr
in std::
namespace. :(
Easiest way might be to define Base like so:
class Base {
public:
virtual const std::string& getData() = 0;
}
And then have your various derived classes implement getData()
as appropriate. That way, your output loop could just be:
for (Base* b : l) {
cout << b->getData();
}
I would redefine Base
to have a virtual method to output the information:
class Base {
friend std::ostream & operator << (std::ostream &os, const Base &b) {
b.print(os);
return os;
}
virtual void print (std::ostream &os) const = 0;
public:
virtual ~Base () {}
};
You are then forced to implement a print
method in each derived class. Your print loop would look like:
for (any_list::iterator itr=l.begin();itr!= l.end();++itr)
std::cout << **itr;
Your list should avoid managing bare pointers, since you put yourself at risk of leaking memory. You can use a smart pointer instead:
typedef std::list< std::unique_ptr<Base> > any_list;
Since unique_ptr
requires explicit construction from the pointer, your code that populates the list needs to be updated.
l.push_back(std::unique_ptr<Base>(new Derived1));
l.push_back(std::unique_ptr<Base>(new Derived2));
l.push_back(std::unique_ptr<Base>(new Derived3));