6

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?

Bo Persson
  • 90,663
  • 31
  • 146
  • 203
GhassanPL
  • 2,679
  • 5
  • 32
  • 40

3 Answers3

8

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.

Bo Persson
  • 90,663
  • 31
  • 146
  • 203
sharptooth
  • 167,383
  • 100
  • 513
  • 979
  • 5
    *Object slicing*, as it is known. – john Aug 26 '11 at 10:57
  • So it's copying objects without copying their VTables? – GhassanPL Aug 26 '11 at 11:15
  • @Kronikarz: It initializes objects of `class Derived` by copying contents of other classes onto them. Of course it doesn't copy vtables - they are immutable. – sharptooth Aug 26 '11 at 11:20
  • 3
    @Kronikarz: Actually, an object does not contain a V Table, only a V Pointer (which is a pointer to its V Table). And no, the V Pointer is never copied. In the Itanium ABI, it changes during construction and destruction only. – Matthieu M. Aug 26 '11 at 11:23
  • You can't copy the vtable pointer. A Child instance can have a bigger than than a Base instance so it cannot be copied into a Base instance. You are using the copy constructor : Base& operator=(Base const& that). By default it copies every field "that.x" of "that" into the corresponding field "this->x" of "*this". But it does not copy the vtable. You cannot copy the vtable pointer (never). What you really want to do in this case is define ar as "Base* ar[]" or (better) "std::unique_ptr ar[]" or "std::shared_ptr ar[]". – ysdx Aug 26 '11 at 11:26
  • @Kronikarz: There is no such thing it does not exist (it is a figment of your imagination. You can't touch it so it is not there). – Martin York Aug 26 '11 at 11:38
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() };
Xion
  • 22,400
  • 10
  • 55
  • 79
0

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.

Rampal Chaudhary
  • 707
  • 1
  • 8
  • 18