c++ upcasting,downcasting

Hi All,

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
  class base
{
     public:
   virtual void display() {cout<<"Base"<<endl;
                              }
};

class derived : public base
{
    public: 
void display(){
   cout<<"derived"<<endl;
}
};

int main()
{
    base*bptr;
    dervied*dptr;

......
...some code here
so that bptr and dptr may contain either base object adress address or derived object address..So I have listed out several possibilities for the type cast.


dynamic_cast<base*> (bptr)   //given that bptr points derived address
dynamic_cast<derived*> (bptr)  //given that bptr points base address
dynamic_cast<derived> (bptr)   //given that bptr points derived address
dynamic_cast<base*> (dptr)             //given that dptr points base address
dynamic_cast<base*> (dptr)             //given that dptr points derived address
dynamic_cast<derived*> (dptr)            //given that dptr points base address


}




which of above typecasts which all are valid and reutn valid address(though I am not picking whatever they return)?
which of them are not valid and return null pointer?
I dont understand the meaning when we typecast derived address in base pointer to either base * type or derived* type.What does that even mean? derived address in base pointer should be treated like a base pointer or derived pointer? in the same way base address in derived pointer should be treated like base pointer or derived pointer?

As a different scenario If I replace all the above dynamic_cast with static_cast will the out come changes or is it same?


Also I have another question related to typecast though its little irrevalant here.

To extract virtual function address in from this pointer we follow below steps
1
2
int*ptr=(int*)this;    
int*vptr_extract=(int*)*ptr;    //they say it extracts vpointer as it(vptr) occupies first 4 bytes of memory 


here I am completely clueless as to why they have typecasted this pointer into int* pointer in the first step.I am not sure why.What will happen if I omit that step?

Also to extract first 4 bytes of the memory they are again typecasting *this into int* again?why its able to return first 4 bytes of the memory?

So If I take a random object and type cast it with int* then also it will return first 4 bytes of memory?

Sorry for these many questions,but I am clueless about typecasting.Please dont suggest to skip typecasting as it may mess up the code and leads memory corruption issues.I would like to know in detail about typecasting.Please help
Last edited on
1) Making derived* pont to base is ilegal. So any case where dptr points to base are illegal and cannot be reasoned about.

2) Null pointers return casts where bptr is casted to derived* and actual object type is base.

If I replace all the above dynamic_cast with static_cast will the out come changes or is it same?
It will work fine in every case where dynamic cast would successd. It will seems to wor in cases where it would be fail, but it is illegal and leads to undefined behavior. Whole purpose of dynamic cast is a "safe" static cast which checs if object we casting, is really of type compatible with result.

Also I have another question related to typecast though its little irrevalant here.
This is technically illegal and works only for specific compiler. It violates strict aliacing rule, causes undefined behavior and makes assumptions about virtual table existence (it will not exist for classes without virtual functions, and compiler is not required to implement runtime dispatch with vtable anyway), about its placement (which is not regulated too), and about correlation between int and pointer sizes (code will break on 64bit systems).
Disregarding all that:
why they have typecasted this pointer into int* pointer in the first step.I am not sure why.What will happen if I omit that step?
They should have casted it to uintptr_t* or void**. They want to cast pointer to object to pointer to some type which can hold a pointer. this points to the beginning of the object, so by casting it they convert it to threat as int (void*, uintptr_t) first bytes of the object.
Then they dereference it to gain access to those bytes and then cast it to pointer, as those bytes are actually a pointer.

Illustration (with void** cast, as it is makes more sense): http://puu.sh/kVCbb/2fb6bd0131.png


So If I take a random object and type cast it with int* then also it will return first 4 bytes of memory?
Probably you would see first bytes. But remember that it is illegal in C++ and can break on you at any time, even when you will compile your code next time.
@MiiniPaa

Thanks a lot for your detailed explanation.However I have few more questions :)

As you said Making derived* pont to base is ilegal. So any case where dptr points to base are illegal and cannot be reasoned about.

(1)So downcasting is illegal in c++? can we ever typecast base address into derived*?


I have eliminated all the cases where derived* points to base address and I am left with below two

dynamic_cast<base*> (bptr) //given that bptr points derived address
dynamic_cast<derived*> (bptr) //given that bptr points derived address

here tricky part is we are trying to type cast derived address stored in base pointer to base* in the first statement .

(2)So how does compiler treats this typecast? is it like its typecasting base pointer into base*(as our address is stored in base pointer only. or does it treat like its typecasting derived address(as actual address in base pointer is derived address) into base pointer?


(3)do both of these typecasts legal?

dynamic_cast<base*> (bptr) //given that bptr points derived address
dynamic_cast<derived*> (bptr) //given that bptr points derived address



Last edited on
These casts are legal and works (returns non-null pointer):
1
2
3
dynamic_cast<base*> (bptr) //given that bptr points derived address
dynamic_cast<derived*> (bptr) //given that bptr points derived address
dynamic_cast<derived*> (dptr) //given that dptr points derived address 

This cast is legal, but do not work (returns null pointer):
dynamic_cast<derived*> (bptr) //given that bptr points base address

So downcasting is illegal in c++? can we ever typecast base address into derived*?
It is illegal to assign pointer to incompatible type:
1
2
3
4
base b;
derived d;
base* bptr = &d; //Legal
derived* dptr = &b; //Illegal. base is not-a derived and cannot be used like it; 

Consider following:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct base
{
    int b = 1;
    virtual void print_a() { std::cout << b << '\n'; }
}

struct derived
{
    int d = 0;
    virtual void print_a() override { std::cout << b + 1 << '\n'; }
    virtual void print_b() { std::cout << d << '\n'; }
}

derived* dptr = (derived*)new base; //Unsafe cast
dptr->print_b();
What should it print? Object we allocated is actually of type base, and do not even have a d member!

Downcasting is perfectly legal if done to correct class. In C++ there are two ways to do that: static_cast, when you absolutely sure that you cast to correct class (no checks are made, if it is not correct class, you have undefined behavior) and dynamic_cast, which does runtime checking and returns null pointer if you are casting it to incorrect (incompatible) type.

dynamic_cast<base*> (bptr) Casts pointer to itself, so it is essentually a no-op.
dynamic_cast<derived*> (bptr) Casts pointer to derived class. Program checks runtime type information stored within object itself to access its actual type and determine if conversion can be made.

(2)So how does compiler treats this typecast? is it like its typecasting base pointer into base*(as our address is stored in base pointer only. or does it treat like its typecasting derived address(as actual address in base pointer is derived address) into base pointer?
As I said it is no-op. Cast whic do not change type of the object is usually just thrown away as it does not change anything. And even if it would not, It still does not matter, as both operation would lead to same result: a pointer, pointing to the base part of object.
Thans again MiiNipaa ,You guys with lots of knowledge are bundle of inspiration for me to learn.

few questions again :)

dynamic_cast<base*> (bptr) Casts pointer to itself, so it is essentually a no-op. //bptr points to derived address


(1)Here though we are typecasting bptr(a base pointer) into base*,the actual address stored there is derived address,so I think
the typecast should be treated like I am trying to convert derived address into base* ? No?

dynamic_cast<derived*> (bptr) Casts pointer to derived class. Program checks runtime type information stored within object itself to access its actual type and determine if conversion can be made.


(2) As you mentioned it checks tun time type identification,could you mention what factors can influence this type cast and what conditions can make this typecast successfully return non null value?

Downcasting is perfectly legal if done to correct class


3)in downcasting which means typecasting derived address into base* , there would always be extra functionality added in the derived class and it would always make it meaningless to do downcasting to convert derived address into base*

But you said its legal if done to correct class? can you help me understand what does this mean?
Last edited on
Here though we are typecasting bptr(a base pointer) into base*,the actual address stored there is derived address,so I think
the typecast should be treated like I am trying to convert derived address into base* ? No?
As I mentioned, it does not matter, result would be still the same: a derived object contains a base subobject inside itself and derived-specific data. By casting derived* to base*, we are returning pointer to that base part. If we already have a pointer to base part, then no matter what actual type of object, such a conversion would always return pointer to that base part
http://puu.sh/kVFPJ/b89c19065b.png

As you mentioned it checks tun time type identification,could you mention what factors can influence this type cast and what conditions can make this typecast successfully return non null value?
The real type of object should be either of same type you are casting it to, or its descendand.

in downcasting which means typecasting derived address into base*
This is upcasting. It is always safe, as any child knows who his parent is. Child→Parent = upcast; Parent→Chiled = downcast. Downcasts could be dangerous as object do not know how many children they have.

there would always be extra functionality added in the derived class and it would always make it meaningless to do downcasting to convert derived address into base*
...Or base class functionality changed. Each derived class is-a base class. This means it supports at least same operation that parent class. This allows it to be used instead of base class. And in presence of virtual functions it allows you to get different behavior depending on real type of object:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
struct Animal
{
    virtual void say() = 0;
    virtual ~Animal() = default;
};

struct Dog : Animal
{
    virtual void say() override { std::cout << "Woof\n"; }
    virtual void bite() {}
};

struct Cat: Animal
{
    virtual void say() override { std::cout << "Meow\n"; }
    virtual void scratch() {}
};
Here any Animal can say: both base class, Dog and Cat; but only Dog can bite and only Cat can scratch. If we cast some derived class pointer to base one, we could use only base class interface (say()), but it will behave according to real type of object:
1
2
3
4
5
Animal* zoo[] = {new Dog, new Dog, new Cat};
for(Animal* animal: zoo) {
    animal->say();
    delete animal;
}
Woof
Woof
Meow


Sometimes we need a extended class functionality. We need to cast downward. One way is to use a static_cast:
1
2
3
4
5
6
Animal* animal = new Cat;
//Works, safe
static_cast<Cat*>(animal)->scratch();

//Compiles, but does not do what you want! Dangerous!
static_cast<Dog*>(animal)->bite();
As you can see, it only works properly when you sure that you are casting it to correct type. If you are not sure, use dynamic_cast, so you can check if conversion can be made:
1
2
3
4
5
6
Animal* animal = new Dog;
Cat* cat = dynamic_cast<Cat*>(animal);
if(cat != nullptr)
    cat->scratch();
else
    std::cout << "This is not a cat\n";
"This is not a cat\n"


Additional examples of how dynamic_cast works:
1
2
3
4
5
6
7
8
9
10
struct Hound: Dog
{};
struct Husky: Dog
{};

Animal* animal = new Husky;
dynamic_cast<Cat*>(animal); //Returns nullptr
dynamic_cast<Dog*>(animal); //Returns non-null pointer of Dog type
dynamic_cast<Husky*>(animal); //Returns non-null pointer of Husky type
dynamic_cast<Hound*>(animal); //Returns nullptr 
dynamic_cast<derived*> (bptr) Casts pointer to derived class. Program checks runtime type information stored within object itself to access its actual type and determine if conversion can be made.


(1)According to what I understood from the above examples ,compiler would take above typecast as it has to convert base pointer(bptr is a base pointer type) into derived*.Why it has to do runtime checks?

These casts are legal and works (returns non-null pointer):
dynamic_cast<derived*> (bptr) //given that bptr points derived addres

This cast is legal, but do not work (returns null pointer):
dynamic_cast<derived*> (bptr) //given that bptr points base address


(2)Why one of them return non null and the other returns null when it seems both of typecasts were same which needed to be converted from
base pointer into derived*?
Last edited on
@MiiniPaa
Sorry I posted the above question before i saw your latest response

Thank you so much It cleared all my confusions on upcasting vs downcasting,the example you mentioned is damn clear and concise.I now really understood why we have to use otherwise dangerous downcast Thanks again! Have a good day!
downcasting and upcasting is legal , prefer using dynamic_cast or static_cast to reinterpret_cast . Use Virtual Pointers , make them a virtual method that return their type , so you wont do a bad casting . Otherwise dynamic_cast returns a null pointer , which you have to check obviously . Note also that when you upcast , you lose information , and when you down casting , if the type is correct , your data will be preserved , otherwise you will have unitialize values , unless you implement assignment operator and copy contructor correctly. The best way is to do so :

class Base{ virtual ~Base();};
class Derived : public virtual Base {virtual ~Derived() final{}};//dont forget virtual !!!!

Base* originalPointer = new Derived; // good
Derived* newPointer = dynamic_cast<Derived*>(originalPointer); //legal

delete newPointer; //legal

delete originalPointer; //error was already freed

As you can see , I am not modifying my original pointer ! (look the assignment above)

to make sure of their type , simply make a virtual method getType() const {return "Derived";} for instance. This is mostly important if you remove RTTI checks from the exe.
Topic archived. No new replies allowed.