I asked a question earlier but it turns out my problem was not properly modeled by my example. So here is my actual problem:
A
, and c
Edit: This answer the first version of the question, now see instead dasblinkenlight's solution.
If you do:
A* b = B();
Then *b
will be of type A. That's what you are doing in your for cycle. There's no "virtuality" or polimorfism involved in this.
The following code gives the behaviour you are looking for:
class A {
public:
virtual void bar() { std::cout << "This is an A" << std::endl; }
};
class B : public A {
public:
virtual void bar() { std::cout << "This is a B" << std::endl; }
};
int main(int argc, char **argv) {
std::list<A *> l;
l.push_back(new B());
l.push_back(new B());
l.push_back(new A());
l.push_back(new B());
for (std::list<A *>::iterator it = l.begin(); it != l.end(); ++it)
(*it)->bar();
}
Taking my example above, in that case:
b->bar();
will print This is a b
.
Others have already explained how it can be achieved.
I'll just limit myself to why it is so.
B gets implicitly cast to A here. So it currently has only properties of A.
The upward casting is implicit in C++.
Downcasting in C++ is possible only if your base class is polymorphic.
In short polymorphic requirement is nothing but something in your base class that can be overridden by your derived!! Virtual methods
then you can use RTTI and dynamic_cast as prescribed by others to do that.
Example:
#include <iostream>
#include <list>
class A {
public:
virtual void dummy() = 0;
};
class B : public A {
public:
void dummy() { }
};
void bar(A &a) { std::cout << "This is an A" << std::endl; }
void bar(B &b) { std::cout << "This is a B" << std::endl; }
int main(int argc, char **argv) {
std::list<A *> l;
l.push_back(new B());
l.push_back(new B());
//Prints A
for (std::list<A *>::iterator it = l.begin(); it != l.end(); ++it)
bar(**it);
//Prints B
for (std::list<A *>::iterator it = l.begin(); it != l.end(); ++it)
bar(dynamic_cast<B&>(**it));
}
Answer:
This is an A
This is an A
This is a B
This is a B
Note: This is only if your list has objects of type B. Otherwise, its going to crash out. This only explains upcast vs downcast
You are looking for run-time polymorphism. This is supported "naturally" for virtual member methods.
An alternative would be to use RTTI and dynamically cast A*
to B*
and call bar
upon success... or static_cast
if you are really sure there are B*
objects. Generally need to down-cast indicates problematic design.
Important note: Run-time check in dynamic_cast requires the type to be polymorphic anyways. Maybe your particular A
fulfills this but you just can't change the class. If not, static_cast
is the only option available.
If you have control over class you, can use standard polymorphism and overload mechanisms using virtual methods on this
as a facade for the "external" call:
#include <iostream>
#include <list>
class A;
void external_bar(A&);
class A {
public:
virtual void bar() { external_bar(*this); };
};
class B;
void external_bar(B&); //IMPORTANT
class B : public A {
public:
virtual void bar() { external_bar(*this); };
};
void external_bar(A &a) { std::cout << "This is an A" << std::endl; }
void external_bar(B &b) { std::cout << "This is a B" << std::endl; }
int main(int argc, char **argv) {
std::list<A *> l;
l.push_back(new B());
l.push_back(new B());
for (std::list<A *>::iterator it = l.begin(); it != l.end(); ++it)
(*it)->bar();
}
This also has drawbacks. Forward declarations are needed. And you need to take care everything is defined properly, because if you forget line // IMPORTANT
the compiler will pick up the definition of external_bar
for A&
as it is implicitly convertible, and you might get quite a headache spotting the error.
Since overloading is resolved at compile time, you need to supply the compiler with enough information to decide on the proper overload of bar
to call. Since you wish to make that decision dynamically based on the run-time type of the object, virtual functions would be of great help:
struct A {
virtual void bar() { bar(*this); }
};
struct B : public A {
virtual void bar() { bar(*this); }
};
It may seem like the bodies are identical, so B::bar
could be eliminated, but this is not true: although the bodies look exactly the same, they call different bar
s due to the static resolution of overloads in C++:
A::bar
the type of *this
is A&
, so the first overload is called.B::bar
the type of *this
is B&
, so the second overload is called.Modify the calling code to call the member bar
will complete the change:
std::list<A *> l;
l.push_back(new B());
l.push_back(new B());
for (std::list<A *>::iterator it = l.begin(); it != l.end(); ++it)
(*it)->bar();
Antonio wrote a good solution involving virtual functions. If you really do not want to use a virtual function for some reason, then you can use dynamic_cast
in a free function instead:
#include <iostream>
#include <list>
struct A {
virtual ~A() {} // important
};
struct B : A {};
void bar(A &a) { std::cout << "This is an A" << std::endl; }
void bar(B &b) { std::cout << "This is a B" << std::endl; }
void bar_helper(A *ptr)
{
if ( auto b = dynamic_cast<B *>(ptr) )
bar(*b);
else
bar(*ptr);
}
int main()
{
std::list<A *> ls;
ls.push_back(new B);
ls.push_back(new B);
ls.push_back(new A);
for (auto ptr : ls)
{
bar_helper(ptr);
delete ptr;
}
ls.clear();
}