2

Following C++ cast to derived class, I encountered something strange. This is the simplification of the problem:

class animal{
public:
    animal(){
        _name="animal";
    }
    virtual void makenoise(){
        cout<<_name<<endl;
    }
    T get_name(){
        return _name;
    };
protected:
    T _name;
};

class cat:public animal{
public:
    cat(){
        this->_name="cat";
    }
private:
};

class dog:public animal{
public:
    dog(){
        this->_name = "dog";
    }
};

If I do a dynamic cast in the following way, it works.

vector<animal*> barnyard;
barnyard.push_back(new animal());
barnyard.push_back(new dog());
barnyard.push_back(new cat());
dog* dogptr = dynamic_cast<dog*>(barnyard[1]);
barnyard[1] = dogptr;
cout<<barnyard[1]->get_name()<<endl;

But doing in the following way:

 for (int ii=0;ii<3;ii++) {
        if (barnyard[ii]->get_name()=="cat"){
            auto dogptr = dynamic_cast<dog*>(barnyard[ii]);
            barnyard[ii] = dogptr;
            cout<<barnyard[ii]->get_name()<<endl;
        }
    }

Gives a segmentation fault. Any idea?

JNo
  • 89
  • 12
  • 2
    Where exactly is it segfaulting (which is the last line to which you can step with your debugger before the segfault occurs)? BTW, your last bit of code will assign a null pointer to `barnyard[2]`. Is that perhaps the cause of the segfault? – Angew is no longer proud of SO Jul 15 '19 at 13:59
  • Where is `T` defined? – eerorika Jul 15 '19 at 14:00
  • Do note that `animal` should provide a virtual destructor: https://stackoverflow.com/questions/461203/when-to-use-virtual-destructors – NathanOliver Jul 15 '19 at 14:00
  • Please edit your question to provide [mcve] – Slava Jul 15 '19 at 14:04
  • 1
    Your example works just fine: https://godbolt.org/z/ENCorA - Please provide code that actually causes a crash, because this code doesn't. Or, if the code I posted there in Godbolt does cause a crash for you, tell us which compiler and environment you're using. – Omnifarious Jul 15 '19 at 14:06
  • @Angew it give me the error at `barnyard[ii] = dogptr;`. Why it should assign a null pointer? – JNo Jul 15 '19 at 14:07
  • @JalilNourisa - What were you expecting dogptr to be in that code, if not `nullptr`? – Omnifarious Jul 15 '19 at 14:08
  • @JalilNourisa because `dynamic_cast` on pointer gives you `nullptr` when it cannot provide such conversion – Slava Jul 15 '19 at 14:08
  • @Omnifarious if i do it outside of the loop, it gives me a dog pointer. – JNo Jul 15 '19 at 14:18
  • 1
    @JalilNourisa - In that specific instance, where you've tested to see that the object is a `cat *` and then `dynamic_cast` to a `dog *`, what do you expect to get? To phrase it another way, what would you expect this code to produce: `dog *dogptr = dynamic_cast(new cat());`? – Omnifarious Jul 15 '19 at 14:21
  • 2
    If you somehow expect `dynamic_cast` to morph a `cat` into a `dog`, you are mistaken. `dynamic_cast(any_pointer_to_a_cat)` will always produce a null pointer, because the conversion will fail. Any subsequent usage of that object (e.g. `barnyard[2]->get_name()`) then dereferences a NULL, and gives undefined behaviour. Many symptoms are then possible, but a program crash is fairly typical. – Peter Jul 15 '19 at 14:30
  • @Peter that's true. Is there any way to do this type of casting possible? this would make my code way more efficient. – JNo Jul 15 '19 at 14:32
  • If a `cat` has a `MeowSound _meowSound;` and a `dog` has a `BarkSound _barkSound;` (or nothing at all), what exactly would you imagine the result of such a type cast to be? If you think your code is "more efficient" by doing something like this, that most likely means that inheritance is not the right tool for the job. – Max Langhof Jul 15 '19 at 14:42
  • @JalilNourisa If you have a distinct set of things which don’t differ in behavior, only differ in data, you probably don’t need multiple classes, enum usually works better. If you have C++/11, use a scoped one e.g. `enum struct eAnimal: uint8_t { Generic, Cat, Dog };` If the reason why you need that cast is to access code or data common between species, move that code/data into the base class and it will work without casts. – Soonts Jul 15 '19 at 14:47
  • @Soonts, please see (https://stackoverflow.com/questions/57042518/a-need-for-dynamic-cast-of-a-derived-class-looking-for-an-alternative-approach) – JNo Jul 15 '19 at 15:07
  • 1
    @JalilNourisa - well, no the casting is not possible. That's why it fails. – Peter Jul 15 '19 at 21:22

1 Answers1

5
if (barnyard[ii]->get_name()=="cat")
    auto dogptr = dynamic_cast<dog*>(barnyard[ii]);

You’re testing the name equals to "cat", and if yes, you’re dynamic-casting to dog. dynamic_cast fails and returns nullptr, this is quite expected.

Soonts
  • 20,079
  • 9
  • 57
  • 130
  • You do not explain what causes segfault – Slava Jul 15 '19 at 14:06
  • 2
    @Slava Something later, obviously. I’ve explained the difference between the 2 snippets of OP’s code, why the first works but second doesn’t. In the first one they’re casting `barnyard[1]` which is a dog. The cat is at index `[2]` in the vector. – Soonts Jul 15 '19 at 14:09