我有一个名为Effect
的基类,其定义如下:
class Effect
{
public:
virtual void apply(int a, int b);
};
void Effect::apply(int a, int b)
{
}
Effect
的一些子类,都是这样定义的:
#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();
我尝试做的是通过apply(int a, int b)
调用effects[whatever].apply(x, y)
的相应重写版本,但我要获得父类'一个而不是。为什么我得到这个结果?
答案 0 :(得分:7)
默认情况下,C ++使用值语义,而不是引用语义,因此effects[0] = SomeSubclassOfEffect();
切片关闭所有子特定信息,只留下父信息。
答案 1 :(得分:4)
c ++将类放在内存中的方式是先将基类放在后面,然后派生类信息。
|base class|derived class|
^ ^ ^
a b c
如果您将其中一个对象的地址作为内存地址a
,则该类的结尾位于此图中的地址c
。派生类中的信息位于从b
到c
的内存段中。这里存储在内存中的apply
的两个版本:基类的版本和派生类的一个版本,它们基于对象的类型被调用。
例如,当您使用Effect effects[3]
创建数组时,您将获得如下所示的内存:
|effect|effect|effect|
C ++只是为你分配足够的空间来容纳每个数组索引effect
类而不再。
现在的问题是你尝试将看起来像这样的对象放到数组的第一个元素中:
|effect|somesubclass|
这不适合一个数组元素,因此c ++ slice 在类的末尾,其中包含有关子类的信息,将其放入丢失信息的数组中。派生类的任何成员现在都不见了!数组的类型为effect
,因此放在数组中的任何内容都将被视为该类型,并且将相应地调用方法。这会导致您遇到的问题。
这称为切片问题:What is object slicing?
为了解决这个问题,你应该永远不要使用多态的对象数组,而是使用指向对象的指针,因为指针大小都相同,因此在数组中使用时会正常工作。指向基类的指针与指向派生类的指针的大小相同,它只是指向内存块开头的指针。指针现在将指向正确的类型,因此将调用正确的函数版本。