I will try to explain how I understand this. the clue that helps me is to think about the parts of the lego.
In your case, we have two lego parts, one of which is called A , and the other is called B ... but imagine that piece B is a piece formed by attaching two parts, one of the parts is the same type A :
AB +-+ +-+-+ |a| |a|b| +-+ +-+-+
Then you use pointers to play each lego part, but each part has its own shape, so 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 , fragment B is different from A , as you can see a little more of its base.
This is the up level that you are talking about, the base pointer is like a wildcard that might contain anything linked down in the inheritance tree.
A * paUpcast = new B ();
Is the line above equivalent to the next?
A * paUpcast = (A *) B;
Well, if you really want to write this: A* paUpcast = (A*) new B(); Yes it is. You create a new instance of class B and save it in a pointer to class A , converting the new instance before assigning it to a pointer does not change the fact that it will be stored in the pointer of the base class in any case.
Why can't we use the following code:
B * pbDowncast = new A ();
B * pbDowncast = (B *) A;
Remember the lego pieces. What happens when executing B* pbDowncast=new A() ?:
B* pbDowncast --> +-+ new A() |a| +-+
By creating a new instance of the base class and storing it in a pointer to a derived class, you are trying to treat the database as a derivative, if you look closely at the lego part, it does not fit! in piece A no additional materials that must be considered in type B ; all this stuff is โstoredโ in the extra part of the lego part, B = all the A stuff plus something more :
B +-+
What happens if you try to call a method that only has class B ? With pointer B , you are allowed to call all methods of B , but the instance you created has the form A , which does not have methods B , it was not created with all these additional things.
However, when we introduce
B * pbDowncast = (B *) pa;
pbDowncast-> F ();
display "A" instead of "B", which causes a contradiction.
This does not contradict me, remembering the parts of lego, the pointer pa points to a piece of type A :
A *pa --> +-+ |a| +-+
This piece is missing all the material B , the fact is that there is no method f() that prints B on standard output ... but it has a method f() that prints A on output.
Hope this helps!
EDIT:
It looks like you also agree that using downcast doesn't it?
No I do not agree. Downcasting is not at all inappropriate, but it will be inadequate depending on its use. Like all C ++ tools, downcasting has a utility and a scope; All tricks that respect good use will be approved.
What would be useful to use the downcasting tool? IMHO everything that would not break the code or the program flow, keeping the code as readable as possible and (most important for me) if the programmer knows what he is doing.
Downcasting with a possible inheritance branch is common practice:
A* paUpcast = new B(); static_cast<B*>(paUpcast)->f();
But that would be troublesome with a more complex inheritance tree:
To handle this, you can use 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 well known for its lack of performance , you can explore some alternatives to dynamic_cast , for example, internal object identifiers or conversion operators, but downcasting is not bad if it is used correctly to adhere to this question.