I am trying to store a number of objects which are derived from a base class in a std::array
(or any other container) without object slicing.
The desired output from the code snippet below is:
Hi from child1!
Hi from child2!
And the code:
#include <iostream>
#include <array>
#include <memory>
class Base {
public:
void hello() {
std::cout << "This shouldn't print\n";
}
};
class Child1 : public Base {
public:
void hello() {
std::cout << "Hi from child 1!\n";
}
};
class Child2 : public Base {
public:
void hello() {
std::cout << "Hi from child 2!\n";
}
};
std::array<std::unique_ptr<Base>, 2> array = {std::make_unique<Child1>(Child1()), std::make_unique<Child2>(Child2()) };
int main() {
for (auto &i : array) {
i->hello();
}
}
The output I actually get from the code is:
This shouldn't print
This shouldn't print
Obviously the derived objects have been sliced. Is there any way to avoid this?
Thanks in advance for any advice you can offer.
CodePudding user response:
Obviously the derived objects have been sliced.
Actually, no. They are not being sliced. You are doing the correct thing in storing base-class pointers to derived-class objects in your array.
No, the code doesn't work the way you want, not because of object slicing, but because you simply did not mark hello()
as virtual
in Base
and as override
in the derived classes. So, when you call i->hello()
inside your loop, there is no polymorphic dispatch being performed.
Also, you are using std::make_unique()
incorrectly. The parameters of make_unique<T>()
are passed as-is to the constructor of T
. In this case, you are copy-constructing your Child1
/Child2
classes from temporary Child1
/Child2
objects, which is not necessary. You can get rid of the temporaries.
You are also lacking a virtual
destructor in Base
. Which will cause undefined behavior when your array goes out of scope and the unique_ptr
s try to call delete
on their Base*
pointers. Only the Base
destructor will be called, but not the Child1
/Child2
destructors.
Try this instead:
#include <iostream>
#include <array>
#include <memory>
class Base {
public:
virtual ~Base() = default;
virtual void hello() {
std::cout << "This shouldn't print\n";
}
};
class Child1 : public Base {
public:
void hello() override {
std::cout << "Hi from child 1!\n";
}
};
class Child2 : public Base {
public:
void hello() override {
std::cout << "Hi from child 2!\n";
}
};
int main() {
std::array<std::unique_ptr<Base>, 2> array = {
std::make_unique<Child1>(),
std::make_unique<Child2>()
};
for (auto &i : array) {
i->hello();
}
}