0

Note: I do not agree this is a duplicate of the linked issue. While that is an issue, that is not the question. Please see below.

When I assign a new class instance to an array, the base class' virtual function is used instead of the overridden derived class virtual function.

I was told the problem with the code below is object slicing. However, according to this YouTube video, this should work. I am a beginner at C++, so I'm not sure who is right. Can someone point me in the right direction or tell me what's wrong?

Is this because I'm copying the object to the array instead of "pointing" to it? However, I can't figure out how to do this correctly.

See code below for what I've tried.

Addendum: There is in fact an object slicing problem here. However, the purpose of my question and what I did not understand is that overriding only works if you use pointers or an array of pointers and not if you declare an array of instances.

#include <iostream>

// I know these should be structs since they're all public!
class Animal {
  public:
    Animal() { std::cout << "Animal constructor\n"; }
    virtual void speak() { std::cout << "Animal/Hello!\n"; }
};

class Dog: public Animal {
  public:
    Dog() { std::cout << "Dog constructor\n"; }
    void speak() override { std::cout << "Dog/Bark!\n"; }
};

int main() {
  const int Size = 4;

  Animal* Kingdom = new Animal[Size];
  Dog* rover = new Dog;

  // Legal because of polymorphism:
  Animal* spot = new Dog;

  // Legal because of polymorphism;
  // Believe this is wrong - I think I'm copying the value rather than
  // pointing to it, but not sure what the right way is...
  Kingdom[0] = *(new Dog);
  // &Kingdom[0] = new Dog;  // Error - left operand must be l-value
  // Kingdom = new Dog;  // Wrong - changing array base, leaking memory

  Kingdom[1] = *rover;
  Kingdom[2] = *spot;

  // Virtual method followed here:
  std::cout << "\nIndividual invocations:\n";
  rover->speak();
  spot->speak();

  std::cout << "\nInvocation from array:\n";
  for (int i = 0; i < Size; ++i)
    // Doesn't work - virtual method not followed, not sure why...
    // Kingdom[i].speak();
    // No difference between these two forms:
    (Kingdom + i)->speak();

  // PS - I know I should put deletes in here not to leak memory!
}

James S.
  • 475
  • 3
  • 10
  • 1
    slicing aside `Kingdom[0] = *(new Dog);` is also a memory leak. You create a dog, copy it and the pointer to the dynamically allocated one is lost, you will never be able to delete it later – 463035818_is_not_an_ai Apr 17 '19 at 13:26
  • _"I think this is because I'm copying the object to the array instead of "pointing" to it. However, I can't figure out how to do this correctly."_ That assumption is correct. _"However, I can't figure out how to do this correctly."_ Use `std::vector> Kingdom(Size);` instead of `Animal* Kingdom = new Animal[Size];` – πάντα ῥεῖ Apr 17 '19 at 13:27
  • You would need an array of `Animal*` (or in modern C++, `std::vector>`). You currently have an array of `Animal`. – Max Langhof Apr 17 '19 at 13:28
  • Thanks - that makes sense. – James S. Apr 17 '19 at 13:37

1 Answers1

1

Your array holds Animal objects, not Animal*. So you don't get any polymorphism and Kingdom[0] = *(new Dog); slices your Dog to just its Animal parts.

Jesper Juhl
  • 30,449
  • 3
  • 47
  • 70