Массив объектов полиморфного базового класса, инициализированных объектами дочернего класса - PullRequest
6 голосов
/ 26 августа 2011

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

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)? Честно говоря, я ожидал, что этот код не скомпилируется, но он это сделал, однако он не дает нужных мне результатов. Это вообще возможно?

Ответы [ 3 ]

8 голосов
/ 26 августа 2011

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

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

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

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

2 голосов
/ 26 августа 2011

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

Base* ar[] = { new ChildA(), new ChildB(), new ChildC() };
0 голосов
/ 27 февраля 2014

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

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

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

  3. ar [0], ar [1] иВиртуальные указатели ar [2] указывают на Base :: Get, поэтому вызывается Base :: Get.

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

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

...