Home > other >  Polymorphic Vectors Without Object Slicing [C ]
Polymorphic Vectors Without Object Slicing [C ]

Time:03-09

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_ptrs 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();
    }
}

Online Demo

  • Related