1

So I have a simple snippet of code:

class Base {
public:
    virtual void print() {cout<<"Base print"<<endl;}
};

class Derived: public Base {
public:
    virtual void print() {cout<<"Derived print"<<endl;}
};

now in my main.cpp, when I downcast a Base class that is instantiated as Derived, I can only downcast if it's a heap variable and not a stack variable, any idea why?

This seems like a simple issue, but I can't find an answer online for it.

Works

int main() {
    
    Base* x = new Derived();
    Derived* d = dynamic_cast<Derived*>(x);
    
    if (d){
        d->print(); // This line executes
    } else {
        cout<<"Cast failed"<<endl;
    }

    return 0;
}

Doesn't Work:

int main() {
    
    Base x = Derived();
    Derived* d = dynamic_cast<Derived*>(&x);
    
    if (d){
        d->print(); 
    } else {
        cout<<"Cast failed"<<endl; // This line executes
    }

    return 0;
}
JoeVictor
  • 1,806
  • 1
  • 17
  • 38
  • `Base* x = Derived();` does not compile on my machine, which is as I'd expect. So the **doesn't work** code never executes the "Cast failed" line. – Eljay Jul 03 '20 at 17:56
  • Updated. My bad. – JoeVictor Jul 03 '20 at 17:57
  • Polymorphism only works via references, pointers or pointer-like objects. Here, `x` is a `Base` that tries to be a copy of `Derived()`, it isn't a `Derived` itself. – François Andrieux Jul 03 '20 at 17:58
  • This has nothing to do with Object Slicing, I am aware of what that is. – JoeVictor Jul 03 '20 at 17:58
  • @QuantumHoneybees Maybe not as well as you think, because `Base x = Derived();` is slicing the `Derived` and the cause of your problem. – François Andrieux Jul 03 '20 at 17:59
  • I've refreshed this three times and seen three different codes that are significantly different in meaning, assuming they compiled. Can you just post the actual code? – Chuu Jul 03 '20 at 17:59
  • No I understand that x is slicing the `Derived` class, in this case that's fine. But why does it fail on the stack vs heap? – JoeVictor Jul 03 '20 at 17:59
  • @Chuu That's not me, i fixed the code only once. It compiles now – JoeVictor Jul 03 '20 at 18:00
  • 1
    @QuantumHoneybees `Base* x = new Derived();` works because it isn't slicing anything. That is the difference. – François Andrieux Jul 03 '20 at 18:00
  • @QuantumHoneybees It's not stack vs heap, it.s pointer vs non-pointer. This `Base x = Derived();` slices because `x` is not a pointer. This `Base* x = new Derived();` doesn't slice because `x` is ` pointer – john Jul 03 '20 at 18:01
  • 1
    The stack example that works is `Derived x; Base* p = &x; Derived* d = dynamic_cast(p);` – john Jul 03 '20 at 18:03
  • @john So basically Object slicing happens based on whatever the type of the data stored on the stack is? Like in your example, x was created on the stack, so it maintains it's `Derived` status even when *the pointer* to it is changed to a `Base*`, but me doing `Base x = Derived()` is equivalent to upcasting and therefore slicing? – JoeVictor Jul 03 '20 at 18:05
  • That make sense because a stack needs to know how much space to allocate for a variable. If it finds that a variable is defined as `Base` it only allocates enough space for a `Base` not `Derived`, so it therefore slices off the variable. @FrançoisAndrieux @john thanks for clarifying. – JoeVictor Jul 03 '20 at 18:08
  • 1
    @QuantumHoneybees Yes although it's worth saying that slicing is a deliberate feature of the language, and happens even if there is enough 'room' in the base class to store an object of the derived class. – john Jul 03 '20 at 18:10
  • 1
    The problem is the copying. It's the act of stuffing a `Derived` into a `Base` rather than referring to a `Derived` as a `Base`. `Base x = Derived()` is copy initialization. A `Base` object is being copy-constructed from a temporary `Derived` object. You could assign after the fact, `Base x; x = Derived();` and get the same slicing. You could also `Base * x = new Base(Derived());` or `Base * x; *x = Derived();` and slice with a dynamically allocated object. Pointing `Base` at `derived` is fine. Storing `Derived` in `Base` will slice, no matter how either were allocated. – user4581301 Jul 03 '20 at 18:37
  • I like this comment a lot, because it also follows that making the copy constructor `Base(const Derived&)` private and deleted is a way to prevent this from compiling and causing trouble. Thanks! – JoeVictor Jul 03 '20 at 19:06

0 Answers0