Array of polymorphic base class objects initialize

2019-01-20 09:44发布

问题:

Sorry for the complicated title. I have something like this:

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());
}

Which outputs:

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

Is this correct behavior (in VC++ 2005)? To be perfectly honest, I expected this code not to compile, but it did, however it does not give me the results I need. Is this at all possible?

回答1:

Yes, this is correct behavior. The reason is

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

initializes array elements by copying objects of three different classes onto objects of class Base and that yields objects of class Base and therefore you observe behavior of class Base from each element of the array.

If you want to store objects of different classes you have to allocate them with new and store pointers to them.



回答2:

To achieve polymorphic behaviour you expected, you should use array of pointers to Base and create objects via new:

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


回答3:

What is actually happening is that:

  1. Since the type of ar[] is Base, 3*sizeof(Base) amount of memory is allocated to ar.

  2. Since you have not declared an explicit copy constructor for Base, the default copy constructor of base is called which just bitwise copies the "Base" part of ChildA, ChildB and ChildC objects into the Base objects contained in the array ar( The default copy constructor is smart enough not to bitwise copy the virtual pointer of Child objects into the Base virtual pointer ).

  3. ar[0], ar[1] and ar[2]'s virtual pointers point to Base::Get so Base::Get is called.

The point to note here is that the function to which virtual pointer of an object will point is always known prior to the execution.

In this case, the runtime knew in advance that arr consists of "Base" objects, so it it set their vptr so as to point to Base::Get as soon as they were alotted memory.