1

In the virtual method create() in the derived class Derived, I return a struct of type HelpDerived. However, since I had to set the return type of the method to HelpBase, I found that I need to cast the returned object back to the type HelpDerived.

The following is an example of my case.


#include <iostream>


struct HelpBase {
    int a = 0;
    virtual void output() {}
};

struct HelpDerived : HelpBase {
    int b = 0;
    void output() override {}
};


class Base {
public:
    virtual HelpBase create() = 0;
};

class Derived : public Base {
public:
    HelpBase create() override;
};

HelpBase Derived::create() {
    HelpDerived d;
    d.a = 1;
    d.b = 2;
    return d;
}


int main() {
    Derived d;
    auto based = d.create();
    HelpDerived derived = dynamic_cast<HelpDerived &>(based);

    std::cout << derived.a << std::endl;
}

When I run the code abve, I get the error

terminate called after throwing an instance of 'std::bad_cast'
  what():  std::bad_cast
Abort trap: 6

What have I misunderstood about objects and casting in C++? Why does this method not work?

What can I do to fix the problem?

landigio
  • 543
  • 1
  • 5
  • 10
  • 1
    You have an object of type `HelpBase` , not `HelpDerived`- I guess you are confused by the object slicing you perform inside the `create` function? – UnholySheep Sep 24 '21 at 12:39
  • 3
    Polymorphism is supposed to work with pointers or references, otherwise you're subject to object slicing. – Fareanor Sep 24 '21 at 12:40
  • 2
    Does this answer your question? [What is object slicing?](https://stackoverflow.com/questions/274626/what-is-object-slicing) – UnholySheep Sep 24 '21 at 12:40
  • 2
    In the `Derived::create` function when you return `d` you [*slice*](https://stackoverflow.com/questions/274626/what-is-object-slicing) the object, leaving you with nothing but the `HelpBase` object being returned. The variable `based` is a `HelpBase` object, there's no way to get back the original `HelpDerived` objects once used in the `create` function. – Some programmer dude Sep 24 '21 at 12:41
  • Thanks, now I at least understand what causes my problem. But Im still not sure how to fix it. Should I make the `create` method return a pointer to a `HelpBase` object instead? – landigio Sep 24 '21 at 12:55
  • Returning a pointer to HelpBase is one possibility. You can even override it with a function that returns a pointer to HelpDerived (google covariant return types). – n. m. could be an AI Sep 24 '21 at 13:57

2 Answers2

1

I think you'd better return a pointer to avoid object slicing in your create function.

#include <iostream>


struct HelpBase {
    int a = 0;
    virtual void output() {}
};

struct HelpDerived : HelpBase {
    int b = 0;
    void output() override {}
};


class Base {
public:
    virtual HelpBase* create() = 0;
};

class Derived : public Base {
public:
    HelpBase* create() override;
};

HelpBase* Derived::create() {
    HelpDerived* d = new HelpDerived;
    d->a = 1;
    d->b = 2;
    return d;
}

int main() {
    Derived d;
    auto based = d.create();
    HelpDerived* derived = dynamic_cast<HelpDerived *>(based);

    std::cout << derived->a << " " << derived->b << std::endl;
    delete derived;
}
Yong-Hao Zou
  • 181
  • 1
  • 4
0

I've replaced dynamic cast by static cast and it worked for me almost fine.

#include <iostream>


struct HelpBase {
    int a = 0;
    virtual void output() {}
};

struct HelpDerived : HelpBase {
    int b = 0;
    void output() override {}
};


class Base {
public:
    virtual HelpBase create() = 0;
};

class Derived : public Base {
public:
    HelpBase create() override;
};

HelpBase Derived::create() {
    HelpDerived d;
    d.a = 3;
    d.b = 2;
    return d;
}


int main() {
    Derived d;
    auto based = d.create();
    HelpDerived derived = static_cast<HelpDerived &>(based);

    std::cout << derived.a << std::endl;
    std::cout << derived.b << std::endl;
}

Output:

3

32726

Note that a value is correct, while b is junk. This is because for derived constructor has not been called. In general, casting from base class to derived is not recommended, as derived may have more data members, than base class. Recommended reading: https://www.bogotobogo.com/cplusplus/upcasting_downcasting.php