从基类型数组 (C++) 中的实例调用重写方法

Calling overriden methods from instances within array of base type (C++)

我有一个名为 Effect 的基 class,其定义如下:

class Effect
{
public:
    virtual void apply(int a, int b);
};

void Effect::apply(int a, int b)
{
}

Effect 的一些子class,都这样定义:

#pragma once

#include "Effect.h" 

class SomeSubclassOfEffect: public Effect
{
public:
    void apply(int a, int b);
};

void SomeSubclassOfEffect::apply(int a, int b)
{ 
    //Magic
}

现在,在我的应用程序的其他部分,我有这个:

Effect effects[6]...

effects[0] = SomeSubclassOfEffect();

我想做的是通过 effects[whatever].apply(x, y) 调用 apply(int a, int b) 的相应覆盖版本,但我得到的是父 class' 版本。为什么我会得到这个结果?

C++ 默认使用值语义,而不是引用语义,所以effects[0] = SomeSubclassOfEffect(); 切片 关闭所有子特定信息,只留下父信息。

c++ 在内存中放置 classes 的方式是先放置基础 class 然后导出 class 信息。

|base class|derived class|
^          ^             ^
a          b             c

如果您采用这些对象之一的地址,即内存地址 a,则 class 的末尾位于此图中的地址 c。你导出的class中的信息在bc的内存段中。这里的内存中存储了 apply 的 2 个版本:一个用于基础 class 的版本,一个用于派生 class 的版本,其中一个被调用是基于对象的类型。 例如,当您使用 Effect effects[3] 创建一个数组时,您会得到如下所示的内存:

|effect|effect|effect|

C++ 只为您分配足够的 space 以适应每个数组索引的 effect class 并且 不再 。 现在的问题是您尝试将看起来像这样的对象放入数组的第一个元素中:

|effect|somesubclass|

这不能放入一个数组元素中,因此 c++ slices 关闭 class 的末尾,其中包括有关子 class 的信息把它放在丢失信息的数组中。 derived class 的任何成员现在都消失了!该数组是 effect 类型,因此数组中的任何内容都将被视为该类型,并且将相应地调用方法。这会导致类似您遇到的问题。

这就是所谓的切片问题:What is object slicing?

要解决这个问题,您永远不应使用对象的多态数组,而应使用指向对象的指针,因为指针的大小都相同,因此在数组内部使用时可以正常工作。指向基 class 的指针将与指向派生 class 的指针大小相同,毕竟它只是指向内存块开头的指针。指针现在将指向正确的类型,因此将调用正确版本的函数。