Массив полиморфных объектов базового класса, инициализированных объектами дочернего класса

извините за сложное название. У меня есть что-то вроде этого:

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: %in", i, ptr->Get());
}

выходы:

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

это правильное поведение (в VC++ 2005)? Честно говоря, я ожидал, что этот код не будет компилироваться, но он это сделал, однако он не дает мне необходимых результатов. Это вообще возможно?

3 ответов


Да, это правильное поведение. Причина

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

инициализирует элементы массива путем копирования объектов трех различных классов на объекты class Base и это дает объекты class Base и поэтому вы наблюдаете за поведением class Base от каждого элемента массива.

если вы хотите хранить объекты разных классов, вы должны выделить их с new и хранить указатели на них.


для достижения полиморфного поведения вы ожидали, вы должны использовать массив указателей на Base и создавать объекты через new:

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

на самом деле происходит следующее:

  1. поскольку тип ar[] является базовым, 3 * sizeof(базовый) объем памяти выделяется ar.

  2. поскольку вы не объявили явный конструктор копирования для Base, вызывается конструктор копирования по умолчанию base, который просто побитово копирует "базовую" часть объектов ChildA, ChildB и ChildC в базовые объекты, содержащиеся в массиве ar( конструктор копирования по умолчанию достаточно умен, чтобы не побитовый скопируйте виртуальный указатель дочерних объектов в базовый виртуальный указатель ).

  3. AR[0], AR[1] и AR[2] виртуальные указатели указывают на Base::Get so Base::Get вызывается.

здесь следует отметить, что функция, на которую будет указывать виртуальный указатель объекта, всегда известна до выполнения.

в этом случае среда выполнения заранее знала, что arr состоит из" базовых " объектов, поэтому она установила их vptr так, чтобы указать на База:: получить, как только они были alotted памяти.