没有对象切片的多态向量 [C++]

Polymorphic Vectors Without Object Slicing [C++]

我正在尝试在 std::array(或任何其他容器)中存储一些从基础 class 派生的对象,而不进行对象切片。

下面代码片段的期望输出是:

Hi from child1!
Hi from child2!

代码:

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

我实际从代码中得到的输出是:

This shouldn't print
This shouldn't print

很明显派生对象已经被切片了。有什么办法可以避免这种情况吗?

提前感谢您提供的任何建议。

Obviously the derived objects have been sliced.

实际上,没有。他们没有被切片。您在将 base-class 指针存储到数组中的 derived-class 对象方面做的是正确的。

不,代码没有按照您想要的方式工作,不是因为对象切片,而是因为您根本没有将 hello() 标记为 virtualBase override中派生类。因此,当您在循环内调用 i->hello() 时,不会执行多态分派。

此外,您使用的 std::make_unique() 不正确。 make_unique<T>()的参数被as-is传递给T的构造函数。在这种情况下,您是 copy-constructing 您的 Child1/Child2 类 来自临时 Child1/Child2 对象,这是没有必要的。你可以摆脱临时工。

您在 Base 中还缺少 virtual 析构函数。当您的数组超出范围并且 unique_ptr 尝试在其 Base* 指针上调用 delete 时,这将导致 未定义的行为 。只会调用 Base 析构函数,不会调用 Child1/Child2 析构函数。

试试这个:

#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