Reimundo Heluani
Reimundo Heluani

Reputation: 978

static cast working and dynamic cast segfaults

The following code compiles and works fine:

#include<iostream>

class Base {
    protected:
        int _a;

    public: 
        virtual ~Base()=default;
        Base(int a) : _a{a} {};

        int getit() const { return _a; }
};

class Derived : public Base {
    public:

        Derived(int a) : Base{a} {};
        int get2() const { return 2*this->_a; }
};

int main() {
    Base D{2};
    auto* ptr = &D;
    auto* ptr2 = static_cast<Derived*>(ptr);


    std::cout << ptr2->get2() << std::endl;
}

OUTPUT

4

If I change the static_cast for a dynamic_cast it segfaults.

My question: Is it safe to use static_cast to cast down to derived classes that do not add any data members?

Upvotes: 1

Views: 457

Answers (2)

eerorika
eerorika

Reputation: 238381

Is it safe to use static_cast to cast down to derived classes

Only if the dynamic type of the object is the derived class (or the derived class is a base of the dynamic type that is even further derived). Casting upwards is also safe, but such conversion also works implicitly.

If you don't know whether that's the case and - as in the example - when you know that isn't the case, this is absolutely not safe, and the behaviour of the example program is undefined. Using dynamic_cast is safe, but not checking whether it returns null, and then indirecting through that null isn't safe.

In conclusion, this would be safe:

if (auto* ptr2 = dynamic_cast<Derived*>(ptr)) {
    std::cout << ptr2->get2() << std::endl;
} else {
    std::cout << "It's not Derived :(" << std::endl;
}

That said, if you think you need dynamic_cast, then you should probably reconsider the design. You should probably be using a virtual function instead. Example:

class Base {
protected:
    int _a;

public: 
    virtual ~Base()=default;
    Base(int a) : _a{a} {};

    virtual int getit() const { return _a; }
};

class Derived : public Base {
public:
    using Base::Base;
    int getit() const override { return 2*this->_a; }
};

Upvotes: 7

Adrian Mole
Adrian Mole

Reputation: 51845

This is a case where using the wrong type of cast is causing you grief. As mentioned in the comments, ptr2 is not pointing to a valid Derived object.

So, with your static_cast, you are seeing undefined behaviour; this just happens to give an apparently 'correct' answer, but you can never rely on that.

However, using a dynamic_cast will result in ptr2 having a nullptr value (because the object pointed to by ptr is not a Derived); your attempt to dereference that nullptr is causing your program to crash (as it most often will).

But note that using the dynamic_cast will allow you to readily check if that cast succeeded (by testing the returned value for nullptr); the static_cast doesn't give you that option.

Upvotes: 1

Related Questions