dynamic cast

Hello I am trying to learn about dynamic cast, and i thought I had worked it out then I came across some pieces of code and I am not sure about it again. Hoping someone could clarify.

So firstly, I think that in the dynamic cast, (first code block below), all that is happening is the base pointer pbb is trying to be changed to a pointer of type dervied and then placed into pointer pd. Is that correct? (I know this cast fails, but just want to know if the idea i have of what is happening is correct?)

1
2
3
4
5
6
7
  CBase * pbb = new CBase;
  CDerived * pd;

   pd = dynamic_cast<CDerived*>(pbb);
    if (pd==0) cout << "Null pointer on second type-cast" << endl;



But then I came across the piece of code below, and it confused me a tad.

What exactly is happening here? Am i casting a reference to a pointer? How does that work? How does a reference become a pointer?

What I am getting from this code is, a reference to object d, is being cast to a base pointer and put into base pointer pb?

1
2
3
4
5
6

CBase b; CBase* pb;
CDerived d; CDerived* pd;

pb = dynamic_cast<CBase*>(&d);     
pd = dynamic_cast<CDerived*>(&b);
not a reference. remember that &x is a pointer, the address of operator. There are no references in this code at all.
so pb is a pointer
that is assigned &d, also a pointer,
with a cast in the middle to resolve the polymorphism.
Last edited on
 
pd = dynamic_cast<CDerived*>(pbb);


This is a downcast. It is casting a base pointer to a derived pointer. This is only good if the specified type is actually derived from the base. Hence the check for pd.

 
pb = dynamic_cast<CBase*>(&d);     


This is an upcast. It is casting a derived pointer to a base pointer. This doesn't actually need a dynamic cast but could be done with a static_cast.

 
pd = dynamic_cast<CDerived*>(&b);


This again is a downcast from base to derived.

See https://en.cppreference.com/w/cpp/language/dynamic_cast
Last edited on


This is a downcast. It is casting a base pointer to a derived pointer. This is only good if the specified type is actually derived from the base. Hence the check for pd.


Just because I am not great with the terminology, this does just mean changing the base pointer to a derived pointer right?

Thank you for the answers, I am going over the address of operator again to refresh myself with it, I have obviously forgotten a lot about it.

Just one question regarding that, in this code, foo would be classified as a pointer then, correct? Since it hold the address of myvar?

myvar = 25;
foo = &myvar;
bar = myvar;
I am actually more lost about the first piece of code than I thought I was. I thought I understood what was going on but after going over it again I do not.

The way i understood it was a derived pointer could point to a derived object or a base object, because derived contains a base part.

But a base pointer could only point to base and not to derived since it was its own object built on top of base (i believe when there is polymorphism involved it can, but ignoring that just now).

But in the code below, why does the second part fail, because it is casting a base pointer to a derived pointer, so end up with a derived pointer, pointing to a base object, which is allowed, is it not? I could understand that it would fail if it was a base pointer pointing to a derived object, but this isnt that.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

class CBase { virtual void dummy() {} };
class CDerived: public CBase { int a; };

int main () {
  try {
    CBase * pba = new CDerived;
    CBase * pbb = new CBase;
    CDerived * pd;

    pd = dynamic_cast<CDerived*>(pba);
    if (pd==0) cout << "Null pointer on first type-cast" << endl;

    pd = dynamic_cast<CDerived*>(pbb);
    if (pd==0) cout << "Null pointer on second type-cast" << endl;

  } catch (exception& e) {cout << "Exception: " << e.what();}
  return 0;
}
Last edited on
pbb is of type CBase* and is set to type CBase*. There is no reference to cDerived.

pd is a pointer to CDerived. So you cannot use dynamic_cast to cast pbb to pd as pbb was never of type CDerived.

pba is of type CBase* but was derived from type CDerived*. Hence you can use dynamic_cast to cast pba to CDerived* as pba was originally of type CDerived*.

For dynamic_cast to work, there must be a
run-time
connection between the two variables. There is between pba and pd but there is not between pbb and pd. That is why the first type-cast works and the second type-cast doesn't.


This is all to do with polymorphism where you have a type of pointer (or reference) to a base class and can set it with a pointer (or reference) to a derived class. Once you have a pointer (or ref) to a base class set by a derived class, then you can convert that base pointer (or ref) back to a derived pointer (or ref) using dynamic_cast.

I know this can take some getting your head around. :)
Thy dynamic cast only succeeds, if the object that is being pointed to actually is an object of the type the pointer is being cast to. In other words, it only succeeds if the cast is going to work properly.

So, in your example, pba is pointing to an object that is, actually, an instance of CDerived - see line 7 - so the dynamic cast succeeds.

However, pbb is pointing an object that is just a CBase, not a CDerived - see line 8 - so it fails. CBase is not a CDerived, so the object pointed to by pbb could not be treated safely as if it were a CDerived - hence dynamic_cast fails.

In other words, dynamic_cast only works if the cast you are trying to do is safe. It's the programmer's friend, because it prevents you making mistakes that will cause your program to crash.
Last edited on
pba is of type CBase* but was derived from type CDerived*. Hence you can use dynamic_cast to cast pba to CDerived* as pba was originally of type CDerived*.


Sorry but what do you mean when you say, it was originally of type derived? How was it of that type originally?

Just so I am not misunderstanding even more than I think I am , CBase * pba = new CDerived;, line is just creating a base pointer and allocating it to point to a derived object on the heap? correct?

Sorry about the repeated question, but for some reason, this is just one thing that I cant seem to figure out
Just so I am not misunderstanding even more than I think I am , CBase * pba = new CDerived;, line is just creating a base pointer and allocating it to point to a derived object on the heap? correct?


Yes, that is correct. The object on the heap is of type CDerived. Even if you store the address of the object in a pointer of type CBase*, that doesn't change what the object at that address actually is.
Last edited on
EDIT: After rethinking about the problem I have spoken about below, I think the animalBase to cat works because the base class still knows about the derived class. And because the function is virtual, it knows it has to call the cats name function and not the base function. Is this correct?

ok so I think I am beginning to understand this a bit more.
Thy dynamic cast only succeeds, if the object that is being pointed to actually is an object of the type the pointer is being cast to. In other words, it only succeeds if the cast is going to work properly.
has helped a lot. But I am still struggling with one part. When I am placing the dynamic cast into a pointer object, shouldnt that pointer need to be of the same type as the object, so example in my code below.

In the second cast, since ani is pointing to a cat type, and is being cast to a cat pointer, shouldnt it need to be placed into a new cat pointer (catDerived, rather than animalBase)? Or does that just not matter? When I run my code it doesnt seem to matter, but just want to check this?
Also how exactly is this working, from what I remember if you set a pointer equal to another pointer without the **, the second pointer simply gets whatever is in the first pointers memory, is that the same with this? Do I end up with a animal pointer pointing at the cat object on the heap? And then and ani pointer still pointing at the cat object too on the heap.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

#include <iostream>
#include "animal.h"	
#include "cat.h"

int main()
{
	animal* ani = new cat;

	animal* ani2 = new animal;


	animal* animalBase;
	cat* catDerived;

	catDerived = dynamic_cast<cat*>(ani);
	if (catDerived == 0)
		std::cout << "null pointer first cast\n";


	animalBase = dynamic_cast<cat*>(ani);
	if (animalBase == 0)
		std::cout << "Null pointer animal base\n";

	animalBase->name();
	ani->name();


	//catDerived = dynamic_cast<cat*>(ani2);
	//if (catDerived == 0)
	//	std::cout << "null pointer second cast\n";
	////dynamic_cast<cat*>(ani2);

	//ani->name();
	//std::cout << '\n';
	//ani2->name();
	
	return 0;
}



1
2
3
4
5
6
7
8
9
10
11
12

#pragma once
#include <iostream>
class animal
{
private:

public:
	virtual void name() { std::cout << "I am an animal"; }

};


1
2
3
4
5
6
7
8
9
10
11
#pragma once
#include "animal.h"

class cat : public animal
{
private:

public:
	void name() { std::cout << "I am an cat"; }
};
Last edited on
the base class still knows about the derived class
No, the base class does not know anything about the derived class.

Each polymorphic class (that contains virtual) additionally stores its run time type information. dynamic_cast uses this type information to determine whether an object is of the desired target type or not. See:

https://www.learncpp.com/cpp-tutorial/12-9-dynamic-casting/
I think the animalBase to cat works because the base class still knows about the derived class.


As coder777 says, this isn't right. The base class doesn't know anything about the derived class.

It is the cat object that you created, that contains the information about what class it is. This is the run time type information (RTTI) that coder777 mentions. When an object is created, and it has virtual functions, then the RTTI is automatically created and stored as part of the object.

If you have an animal pointer, and the pointer is pointing to a cat object, then when you use that pointer to call a virtual method like name(), then the behind-the-scenes code looks at the RTTI, discovers that the object is actually a cat, and calls cat::name() - even though the pointer you used to access the object is only an animal*.

We call this run-time polymorphism - that is, the ability of the program to determine at run-time what the type of the object really is, even if you're using a pointer of a base class type.

When I am placing the dynamic cast into a pointer object, shouldnt that pointer need to be of the same type as the object, so example in my code below.

No. It just needs to be a compatible pointer type. By definition, a cat object is also an animal object, so it is a compatible type. Any attempt to treat the object as an animal will work, because cat has all the properties of animal.

from what I remember if you set a pointer equal to another pointer without the **, the second pointer simply gets whatever is in the first pointers memory, is that the same with this?

Both pointers are pointing to exactly the same bit of memory, yes.

Remember: a pointer is just a number, and that number is a memory address. If you set two pointers to store the same value, then they are both storing the same memory address, which means they're both pointing to the same bit of memory.

Do I end up with a animal pointer pointing at the cat object on the heap? And then and ani pointer still pointing at the cat object too on the heap.

Yes. At the end of your code, ani, animalBase and catDerived all point to exactly the same object, and that object is a cat.

Last edited on
Topic archived. No new replies allowed.