I would try to explain how i understand it. the tip that helps me is to think about lego pieces.
In your case, we have two lego pieces, one named A
and another named B
... but just imagine that the B
piece is a piece formed by attaching two pieces, one of the pieces is the same type of A
:
A B
+-+ +-+-+
|a| |a|b|
+-+ +-+-+
Then, you use pointers to reffer each of the lego pieces, but each piece has its own shape, so, just imagine:
A* pa =new A();
B* pb =new B();
A* paUpcast= new B();
A *pa --> +-+ new A()
|a|
+-+
B* pb --> +-+-+ new B()
|a|b|
+-+-+
A* paUpcast --> +-+-+ new B()
|a|b|
+-+-+
Note that the paUpcast
pointer is a pointer of type A
but holding a piece of type B
, the B
piece is different from the A
one, as you can see is a piece slightly greater than its base.
This is the upcasting you're talking about, the base pointer is like a wildcard that can hold anything related downwards on the inheritance tree.
A* paUpcast= new B();
Is the line above equivalent to the following?
A* paUpcast = (A*) B;
Well, assuming that you really want to write this: A* paUpcast = (A*) new B();
yes, it is. You create a new instance of the B
class and stores it into a pointer to A
class, converting the new instance before assigning into the pointer doesn't changes the fact that it will be stored into a base class pointer anyway.
Why we cannot use the following 2 code;
B* pbDowncast=new A();
B* pbDowncast = (B*) A;
Remember the lego pieces. What happens while doing B* pbDowncast=new A()
?:
B* pbDowncast --> +-+ new A()
|a|
+-+
Creating a new base clas instance and storing it into a pointer to derived class, you're trying to treat the base as derived, if you look closely the lego piece doesn't fit! the A
piece lacks of the extra stuff necessary to be considered of the B
kind; all this stuff "is stored" into the extra part of the lego piece, B = all the A stuff plus something more
:
B
+-+-----------------------------------+
|a|extra stuff that only B pieces have|
+-+-----------------------------------+
What would happen if you try to call a method that only the B
class has? Having a B
pointer you're allowed to call all the B
methods, but the instance you created is from A
kind that wouldn't have the B
methods, it wasn't created with all this extra stuff.
However, when we type
B* pbDowncast=(B*)pa;
pbDowncast->f();
display "A" instead of "B", which make the contradiction happen.
It doesn't ressembles a contradiction to me, remembering the lego pieces, the pa
pointer is pointing to a piece of type A
:
A *pa --> +-+
|a|
+-+
This piece lacks of all of the B
stuff, the fact is that lacks the f()
method that prints B
on the standard output... but it have a method f()
that prints A
on the output.
I hope it helps!
EDIT:
It seems that you also agree that it is inappropriate to use the downcast isn't it?
No, i don't agree. Downcasting isn't inappropiate at all, but it would be inappropiate depending on its use. Like all the C++ tools, the downcasting has an utility and scope of use; all the trickery that respect the good use would be appropiate.
What would be a good use of the downcasting tool then? IMHO anything that wouldn't break the code or the program flow, maintaining the code as readable as possible and (the most important for me) if the programmer knows what is he doing.
Downcasting taking a possible inheritance branch is a common practice after all:
A* paUpcast = new B();
static_cast<B*>(paUpcast)->f();
But it would be troublesome with a more complex inheritance tree:
#include<iostream>
using namespace std;
class A{
public:
virtual void f()
{
cout<<"A"<<endl;
}
};
class B: public A{
public:
virtual void f()
{
cout<<"B"<<endl;
}
};
class C: public A{
public:
virtual void f()
{
cout<<"C"<<endl;
}
};
A* paUpcast = new C();
static_cast<B*>(paUpcast)->f(); // <--- OMG! C isn't B!
To deal with this, you can get use of the dynamic_cast
A* paUpcast = new C();
if (B* b = dynamic_cast<B*>(paUpcast))
{
b->f();
}
if (C* c = dynamic_cast<C*>(paUpcast))
{
c->f();
}
But dynamic_cast
is well known by it's lack of performance, you can study some alternatives to dynamic_cast
, like internal object identifiers or conversion operators but in order to stick to the question, the downcasting isn't bad at all if it is used correctly.