Подтвердить что ты не робот

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

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

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

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

4b9b3361

Ответ 1

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

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

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

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

Ответ 2

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

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

Ответ 3

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

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

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

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

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

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