The statement:
Base *ptr = new Base;
Doesn't always allocate sizeof(Base)
- it would probably allocate more memory. Even if it does allocate exact sizeof(Base)
bytes, it doesn't necessarily mean any byte access after this range (i.e. sizeof(Base)+n
, n>1) would be invalid.
Hence let's assume the size of class Base is 4 bytes (due to virtual function table in most compiler's implementation, on a 32-bit platform). However, the new
operator, the heap-management API, the memory management of OS, and/or the hardware does allocate 16 bytes for this allocation (assumption). This makes additional 12
bytes valid! It makes the following statement valid:
static_ptr->i = 10;
Since now it tries to write 4 bytes (sizeof(int)
, normally) after the first 4 bytes (size of polymorphic class Base
).
The function call:
static_ptr->foo();
would simply make a call to Derived::foo
since the pointer is of type Derived
, and nothing is wrong in it. The compiler must call Derived::foo
. The method Derived::foo
doesn't even try to access any data member of derived class (and even base class).
Had you called:
static_ptr->do_derived();
which is accessing i
member of derived. It would still be valid, since:
- The function call is always valid, till method tries to access data-member (i.e. accesses something out of
this
pointer).
- Data-member access became valid due to memory allocation (UD
behaviour)
Note that following is perfectly valid:
class Abc
{
public:
void foo() { cout << "Safe"; }
};
int main()
{
Abc* p = NULL;
p->foo(); // Safe
}
The call it valid, since it translates to:
foo(NULL);
where foo
is:
void foo(Abc* p)
{
// doesn't read anything out of pointer!
}