using static_cast to downcast

I have been handed a project in which mostly static_cast's are used for down-casting. I always did dynamic_cast so this was surprising and i went on to study a little more about these casts as i had limited knowledge. Now based on the articles on the net and the way the project has been coded, i've formed certain ideas on when to use which. Please let me know if there's anything wrong.

<1> In general dynamic_cast's are more expensive than static_cast. Also dynamic_cast's are compile time error if the inheritance is non-polymorphic (no virtuals). static_cast on the other hand will allow you to treat the memory as if it belonged the type being casted to, so if the original type was of base-class type and is being static_casted to derived type then access to any additional data that contributes to the sizeof() operator in the derived class will be undefined (something like memory corruption due to size overflow).
eg.:
1
2
3
4
5
6
7
8
9
class A {/*data*/};
class B : public A
{
   public:
     int additionalData;
};
A* ptrA=new A;
B* ptrB=static_cast<B*>(ptrA);
ptrB->additionalData = 0; // undefined 


<2> However if the only reason is that you have actually extended the functionality by addition of functions (and members that don't contribute to sizeof()) in the derived class then we should always go for static_cast as it's faster (and ofcourse in non-polymorphic inheritance there is no other way as dynamic_cast won't work).
eg.:
1
2
3
4
5
6
7
8
class C : public A
{
  public:
    int additionalData;
    void additionalFunction();
};
C* ptrC=static_cast<C*>(ptrA);
ptrC->additionalFunction(); // fine 


<3> the above <2> will give an unexpected behaviour if additionalFunction() is a virtual in A, in which case polymorphism won't work and vtable of A will not have been replaced by those of C, so it'll end up calling additionalFunction() of A.

<4> would dynamic_cast resolve the issue in <3>?

please comment on the 4 points i've written above. It'll clarify some casting concepts for me
Last edited on
<1> The first two sentences are correct. dynamic_cast needs a class to be polymorphic. It also is generally more expensive. The rest, though....

sizeof() has absolutely nothing to do with how the cast behaves or whether or not it's safe/successful.

Also, static_cast will fail unless the types are related. Example:

1
2
3
4
5
class A {};
class B {};

A* a = new A;
B* b = static_cast<B*>(a);  // compiler error, A and B are not related 


<2> This is false. The only time you should "always" use static_cast is if you know for a fact the cast will be safe. Again whether or not it's "safe" has nothing to do with the sizeof() the class. See notes at the bottom.


<3> This is false. static_cast will correctly adjust the vtable as needed.


<4> N/A, there is no issue in <3>.




Really, you are way overcomplicating things here. static_cast and dynamic_cast are quite simple. They are actually identical except for the below key points:

1) dynamic_cast does a runtime check to make sure the cast is good. This means it's generally slower.
2) Also, because dynamic_cast does the runtime check, it's much safer. There is zero risk of a bad cast going unnoticed.
3) dynamic_cast requires the class to be polymorphic. static_cast does not.


The only difference is the runtime check. But both casts cast the pointer the same way.

As for what a bad cast is...

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
class Parent
{
public:
virtual ~Parent() {}  // make it polymorphic
};

class A : public Parent { };
class B : public Parent { };

int main()
{
  A a;
  B b;

  Parent* p = &a;  // p points to an 'A' object

  // how dynamic cast works:
  A* dyn_a = dynamic_cast<A*>(p);  // dynamic_cast checks 'p' to make sure it points to an 'A'
     // it does, so this cast is safe.  dynamic_cast succeeds.


  B* dyn_b = dynamic_cast<B*>(p);  // dynamic_cast checks 'p'.  It will see that 'p' does not
    // point to a 'B', so this is a bad cast.  dynamic_cast will fail and dyn_b will be == nullptr


  // how static_cast works
  A* sta_a = static_cast<A*>(p);  // static_cast assumes the cast is good.  No runtime check
    // sta_a will be identical to dyn_a above

  B* sta_b = static_cast<B*>(p);  // static_cast assumes the cast is good... BUT IT ISN'T
    //  'p' does not point to a 'B', so this is a bad cast!  static_cast succeeds, resulting in
    //  'sta_b' being a BAD POINTER.  Dereferencing sta_b has undefined behavior.
}



That's the difference.

Really... this means that when you have to downcast:

- use static_cast only if you are 100% sure the cast is good.
- use dynamic_cast if you are not sure.
B* sta_b = static_cast<B*>(p); // static_cast assumes the cast is good... BUT IT ISN'T
// 'p' does not point to a 'B', so this is a bad cast! static_cast succeeds, resulting in
// 'sta_b' being a BAD POINTER. Dereferencing sta_b has undefined behavior.


Is Dereferencing sta_b always an undefined behaviour or is it undefined if calling members of B not in Parent?

eg.:
1
2
3
4
5
6
7
8
class B : public Parent
{
   public:
     int notInParent;
     void notInParentFunc() { }
};
sta_b->notInParent=0; //<1>
sta_b->notInParentFunc(); //<2> 


are <1> and <2> both undefined or just <1>?
any kind of dereferencing is undefined when you have a bad pointer.
Topic archived. No new replies allowed.