【问题标题】:Array of polymorphic base class objects initialized with child class objects用子类对象初始化的多态基类对象数组
【发布时间】:2023-03-16 09:34:01
【问题描述】:

抱歉标题太复杂了。我有这样的事情:

class Base
{
public:
  int SomeMember;
  Base() : SomeMember(42) {}
  virtual int Get() { return SomeMember; }
};

class ChildA : public Base
{
public:
  virtual int Get() { return SomeMember*2; }
};

class ChildB : public Base
{
public:
  virtual int Get() { return SomeMember/2; }
};

class ChildC : public Base
{
public:
  virtual int Get() { return SomeMember+2; }
};

Base ar[] = { ChildA(), ChildB(), ChildC() };

for (int i=0; i<sizeof(ar)/sizeof(Base); i++)
{
  Base* ptr = &ar[i];
  printf("El %i: %i\n", i, ptr->Get());
}

哪些输出:

El 0: 42
El 1: 42
El 2: 42

这是正确的行为吗(在 VC++ 2005 中)?老实说,我希望这段代码不会编译,但它确实编译了,但它并没有给我我需要的结果。这有可能吗?

【问题讨论】:

    标签: c++ visual-studio-2005 initialization polymorphism virtual


    【解决方案1】:

    是的,这是正确的行为。原因是

    Base ar[] = { ChildA(), ChildB(), ChildC() };
    

    通过将三个不同类的对象复制到class Base 的对象上来初始化数组元素,这会产生class Base 的对象,因此您可以从数组的每个元素中观察class Base 的行为。

    如果您想存储不同类的对象,您必须使用new 分配它们并存储指向它们的指针。

    【讨论】:

    • 对象切片,众所周知。
    • 所以它是在复制对象而不复制它们的 VTable?
    • @Kronikarz:它通过将其他类的内容复制到class Derived 的对象上来初始化它们。当然它不会复制 vtables - 它们是不可变的。
    • @Kronikarz:实际上,一个对象不包含 V 表,只有一个 V 指针(指向其 V 表的指针)。不,V 指针永远不会被复制。在 Itanium ABI 中,它仅在构建和销毁过程中发生变化。
    • 您不能复制 vtable 指针。 Child 实例的大小可以大于 Base 实例,因此不能将其复制到 Base 实例中。您正在使用复制构造函数:Base& operator=(Base const& that)。默认情况下,它将“that”的每个字段“that.x”复制到“this”的相应字段“this->x”中。但它不会复制 vtable。您不能复制 vtable 指针(从不)。在这种情况下,您真正​​想做的是将 ar 定义为“Base ar[]”或(更好)“std::unique_ptr ar[]”或“std::shared_ptr ar[ ]"。
    【解决方案2】:

    要实现您期望的多态行为,您应该使用指向Base 的指针数组并通过new 创建对象:

    Base* ar[] = { new ChildA(), new ChildB(), new ChildC() };
    

    【讨论】:

      【解决方案3】:

      实际发生的是:

      1. 由于ar[]的类型是Base,所以分配了3*sizeof(Base)的内存给ar。

      2. 由于您没有为 Base 声明显式复制构造函数,因此调用 base 的默认复制构造函数,它只是将 ChildA、ChildB 和 ChildC 对象的“Base”部分按位复制到数组中包含的 Base 对象中ar(默认的复制构造函数足够聪明,不会将Child对象的虚拟指针按位复制到Base虚拟指针中)。

      3. ar[0]、ar[1] 和 ar[2] 的虚拟指针指向 Base::Get,因此 Base::Get 被调用。

      这里要注意的是,对象的虚拟指针所指向的函数在执行之前总是已知的。

      在这种情况下,运行时预先知道 arr 由“Base”对象组成,因此它设置它们的 vptr 以便在分配内存后立即指向 Base::Get。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-10-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-03-19
        • 1970-01-01
        • 2021-04-24
        相关资源
        最近更新 更多