tags:

views:

171

answers:

4
+10  Q: 

Confusion in output

Consider the following program.

#include<iostream>
using namespace std;

class base
{

   public:
     int _bval;

     base():_bval(0){}
};

class derived:public base
{

   public:
     int _dval;

     derived():base(),_dval(1){}
};

int main()
{     

   derived d[5];
   base *p;
   p=d;
   for(int i=0;i<5;++i,++p)
      cout<<p->_bval;

}

The output of the above program is 01010.
I thought the output would be 00000 because the value of _bval was initialized to 0(each time) by the base class constructor.

But why is the output different from 00000?
What am I missing?

+11  A: 

p[i] gives you the value at sizeof(base) * i bytes after p. So p[1] won't give you the second element of d, it will give you the second half of the first element.

In other words: if you use a pointer to the base class to iterate over an array of the derived class, you'll get wrong results if the derived class has a bigger size than the base class because it will iterate in steps of sizeof(baseclass) bytes.

sepp2k
You are right, if we change `i<10`we will se more `01` :)
Rin
But how can _bval's value be '1'?
Prasoon Saurav
`bval` is the only member variable of class b. So to get the `bval` of a `base` object you take the value at `address_of_the_base_object + 0`. So when p is pointing at the second half of a `derived` object, it will do `p + 0` to get calculate the address of `bval`, which in this case will actually return the adress of `dval` because `p` is not really pointing at a `base` object.
sepp2k
Yeah I think I got it now.The confusion was because the pointer to the base class was iterating over an array of derived class objects.So the result was not as expected.
Prasoon Saurav
+3  A: 

Think of the memory layout of d array.

d->0101010101

Where each pair of 01 correspond to one derived object.

Now let p point to it:

p->0101010101

Since the size of base objects is that of one int. That memory segment is considered to be 10 base objects: the first one with _bval 0, the second one with _bval 1,...etc.

Neat! Showing the memory layout and explaining the base pointer semantic is very clear. +1 :)
legends2k
+1  A: 

Beside what sepp2k said, you didn't initialize _bval in the derived class constructor. You should initialize it using base constructor.

AraK
+7  A: 

Short answer: In C++, arrays of values are never polymorphic, even if their content is, and cannot be treated so. That is, you cannot treat an derived ad[N] as if it was a base ab[N].

Long answer: The reason for this is deeply buried in C's pointer arithmetic. If you have an int* pi and increment it ++pi, it will not simply increment to the next memory address. If it did, it wouldn't point to the next int since this doesn't start at the next address. So instead sizeof(int) bytes are added to the pointer. (A concrete example might help: On architectures with 8bit char types - char being, by definition what C and C++ consider the architecture's byte size - and 32bit int types, int has the size of 4 bytes. Thus, ++pi will add 4 to the pointers address, so that it points to the next int.) The same arithmetic applies to all other pointer operations. So, for example, with int* pi2=pi+1, pi2 will point sizeof(int) bytes behind pi, although pi2-pi will yield 1.

So, presuming you understood the last paragraph, let's go back to arrays. If you have an array derived ad[N], the address of ad[1] is sizeof(derived) bytes greater than the address of ad[0]. (That's disregarding alignment in order to not to further complicate the issue.) However, if you have a base* pb pointing to ad[0], incrementing it will make it point sizeof(base) behind the address of the first element - which, if (as is the case in your example) sizeof(base) < sizeof(derived), is not the address of ad[1], but somewhere in the middle of ad[0].

The only thing you can do to treat the array content as if it was all base classes, is to iterate over the array using a derived* and cast this pointer to base* within the loop:

derived d[5];
derived* begin = d; 
const derived* end = d + sizeof(d)/sizeof(d[0]); // points one beyond the last element
while(begin != end)
{
  base* pb = begin;
  cout<< pb->_bval;
  ++begin;
}

(Note that I've also changed your code to use C++' idiomatic begin/end iterators.)

sbi
Thanks for being precise.
Prasoon Saurav