1

I am trying to figure out what happens when you dynamic_cast from one derived class to another derived class. Why does the below code raise a segmentation fault? Note that I am not trying to use this code for anything. I merely want to understand what is happening.

It's also worth noting that the same code works using static_cast. I can't seem to find any documentation that goes into the finer details of what is happening here. Can somebody explain?

struct base 
{ 
    virtual void printing(){cout<<"base printing"<<endl;};
};
struct derived_1 :public base 
{ 
    virtual void printing(){cout<<"derived_1 printing"<<endl;};
};
struct derived_2 :public base 
{ 
    virtual void printing(){cout<<"derived_2 printing"<<endl;};
};

int main()
{
    base * b = new derived_2();
    derived_1 *d_1 = dynamic_cast<derived_1*>(b);

    // calling printing raises segmentation fault
    d_1->printing(); 
}
Arran Duff
  • 1,214
  • 2
  • 11
  • 23
  • 2
    Note: Albeit legal, you should remove the `virtual` specifier from the overriden methods in the subclasses and add `override` instead, as in `void printing() override { ... }`. – andreee May 30 '18 at 11:23

1 Answers1

1

The cast to derived_1 will fail, because derived_2 is a base object but not a derived_1 object. Therefore, you cannot "side cast" to the desired pointer type.

Not that whenever dynamic_cast fails, it will return a nullptr (except for reference types). This eventually leads to the segmentation fault in your code (in general, I would suggest you always add a if (d_1 != nullptr) before using a dynamically casted object).

Update:

By the way, this is actually a good example for the necessity of dynamic_cast. Even though you could be tempted to use static_cast in your example and it would compile, you will be dealing with undefined behavior. Using static_cast will compile without hickups, but you would actually be working with a broken type. Assume derived_1::printing() accesses some variable derived_1::a, which does not exist in derived_2. By statically side-casting a derived_2 object (which doesn't have a) to a derived_1 object d_1, you would falsely assume d_1 to contain some valid a, which is not the case.

Example:

// ...
struct derived_1 :public base
{
    const int a = 123;
    void printing() override {cout<<"derived_1 printing " << endl;}
    void foo() { cout << "Foo using constant = " << a << endl; }
};

// ...
int main()
{
    base * b = new derived_2();
    derived_1 *d_1 = static_cast<derived_1*>(b);
    d_1->printing(); // Will call derived_2::printing(), which is not what you expect!
    d_1->foo();      // Won't show a = 123 but some nonesense (mostly 0),
                     // because a is not defined in derived_2.
}
andreee
  • 4,459
  • 22
  • 42
  • Ah ok that makes sense! Thanks for the help. I was getting confused because the code I was actually testing was calling 2 functions. So it read like d_1->non_virtual_member_function_of_derived_1(); d_1->printing(); The first function call was succeeding so I just assumed that d_1 was not a nullptr. However, I see now that nullptr's can actually call member functions. Even though this is undefined behaviour it was succeeding in this case – Arran Duff May 30 '18 at 12:05
  • _However, I see now that nullptr's can actually call member functions._: No, you can't call anything on a nullptr. – andreee May 30 '18 at 12:12
  • That's what I thought but apparently you can. It is undefined behaviour, but you can do it and it will not always raise segmentation fault. This is discussed at this SO [link](https://stackoverflow.com/questions/11320822/why-does-calling-method-through-null-pointer-work-in-c) – Arran Duff May 30 '18 at 13:33
  • You can call non-virtual functions on null pointers, though it is classed as undefined behaviour. However calling virtual functions with a null pointer will be fatal, as knowledge of the object is required to look up the implementation of the virtual function - generally via the VFT pointer in the instance itself. – Gem Taylor May 30 '18 at 16:20