为什么一个对象在被推入容器时会失去其多态性?

时间:2015-10-29 11:29:22

标签: c++ polymorphism

child

派生的对象base
class base
{
    public:
    virtual int get() const { return 0;}
    ~virtual base(){}
    };

    class child : public base
    {
    public:
    virtual int get() const override { return 1; }
};

在添加到容器时被切片,例如

std::vector<base> bases;
bases.push_back(child());

,下面的代码返回0

bases.at(0).get();

即使push_back的签名是:

void push_back( const T& value );
void push_back( T && value );

不应该招致要切片的对象。

例如,下面的代码会导致bad_cast

dynamic_cast<child &>(bases.at(0))

而与基地的孩子一起打电话会表现出多态行为

void toto(base const & e)
{
    std::cout << e.get() << std::endl;
}

暗示对象不再是孩子;切片在哪里?究竟发生了什么?

是否可以设置basechild以便放置在容器中并且对象不会失去其多态性?

这是一个基本原理问题,并且已经知道一个对象被复制了,所以请不要建议指针/ shared_ptr,因为这已在其他地方得到解决。

3 个答案:

答案 0 :(得分:6)

  

切片在哪里?

std::vector<base> bases;

存储普通base个对象。您无法在其中添加任何其他内容,只能添加base个对象。因此,如果你在那里添加了一个对象,那么它将是一个base对象。如果您将其他类型传递给push_back,则会将其转换为base(如果可能);在你的情况下,这意味着切片。我不认为您对特定实现的std::vector函数内部切片的确切位置感兴趣。

  

是否可以设置basechild以应对此问题   放在容器中,物体不会丢失它   多态性?

为了保持多态性,您需要存储指向base s 1 的指针。除了容器之外,还有很多方法可以复制它。最简单的方法是编写类似

的内容
child c;
...
std::vector<base*> v;
v.push_back(new child(c)); // and yes, smart pointers are better

您当然可以为push_back添加包装,以避免每次都输入new

1您无法在向量中存储引用,但请参阅this question以获取更多选项。

答案 1 :(得分:3)

  

即使push_back的签名是:

     

不应该招致要切片的对象。

这是事实。

  

例如,下面的代码会导致bad_cast
  dynamic_cast<child &>(bases.at(0))

是的,确实如此。并且dynamic_cast不在std::

  

而与基地的孩子一起打电话会表现出多态行为

void toto(base const & e)
{
std::cout << e.get() << std::endl;
}

是的,确实如此。

  

切片在哪里?究竟发生了什么?

问题本身不是push_back切片push_back 分配传递给内部对象的内容时发生,不是多态的,而不是在参数本身被绑定时。

要保持多态行为,必须使用指针(最好是智能指针)或引用;但是,普通引用不能存储在容器中,您必须使用它们的包装器,如std::reference_wrapper

来自Stephen Li的

There is a very comprehensive (and imho underrated) answer关于多态性为何如此运作的原因。

答案 2 :(得分:2)

这不起作用:多态性使用VTables,它存储指向您类的方法的指针。问题:如果复制它,VTable指针将被设置为基类地址。

此外它无法工作,因为派生类可以作为基类更大,因此无法计算内存消耗。但是容器需要知道对象有多大。