Friday, September 07, 2007

Inside the c++ object model (1)

Library_materials thing1;

class Book: public Library_materials{...}
Book book;

thing1 = book; // book is sliced and thing1 remains Library_materials
thing1.check_in(); // invokes Library_materials::check_in()

Library_materials &thing2 = book; // thing2 references book
thing2.check_in(); // invokes Book::check_in()


Only the indirect manipulation of the object through a pointer or reference supports the polymorphism for OO programing. The actual type of the object addressed is not resolved in principle until runtime at each particular point of execution. For instance:
Library_materials *px = retrieve_some_material();
Library_materials &rx = *px;
Library_materials dx = *px;

It can never be said with certainty what the actual type of the object is that px or rx addresses. It can only be said that it is either a Library_materials object or a subtype rooted by Library_material class. dx can only be an object of the Library_materials class.

C++ supports polymorphism in following ways:
  1. Through a set of implicit conversion, such as the conversation of a derived class pointer to a pointer of its public bas type: shape *ps = new circle();
  2. Through the virtual function mechanism.
  3. Through the dynamic_cast and typeid operators: if ( circle *pc = dynamic_cast<> ( ps ) ) ...

The memory requirements to represent a class object :
  1. the accumulated size of its nonstatic date members
  2. Plus any padding due to alignment constraints
  3. Plus any internally generated memory to support virtuals
The memory requirements to represent a pointer is a fixed size regardless of the type it addresses.


class ZooAnimal{
public:
ZooAnimal();
virtual ~ZooAnimal();
// ...
virtual void rotate();

protected:
int loc;
String name;
};

class Bear:public ZooAnimal {
public:
Bear();
~Bear();
// ...
void rotate();
virtual void dance();
//...
protected:
enum Dances {...};
Dances dances_known;
int cell_block;
};

What are the differences between a Bear and ZooAnimal pointer?
Bear b;
ZooAnimal *pz = &b;
Bear *pb = &b;


Each addresses the same first byte of the Bear object. The difference is that the address span of pb encompasses the entire Bear object, while the span of pz encompasses only the ZooAnimal subobject of Bear. pz cannot directly access any members other than those present within the ZooAnimal subobject, except through the virtual mechanism.
pz->cell_block; //illegal: cell_block is not a memeber of ZooAnimal
(( Bear* )pz)->cell_block; // OK: an explicit downcast
if ( Bear *pb2 = dynamic_cast<> (pz) ) pb2->cell_block; // better
pb->cell_block; // OK, it is a member of Bear


Given:
Bear b;
ZooAnimal za = b;

za.rotate();


If memberwise initialization copies the values of one object to another, why is za's vptr not addressing Bear's virtual table?

-- The compiler intercedes in the initialization and assignment of one class object with another. The compiler must ensure that if an object contains one or more vptrs, those vptr values are not initialized or changed by the source object.

Given a set of definitions:
{
ZooAnimal za;
AooAnimal *pza;

Bear b;
Panda *pp = new Panda;

pza = &b;
}


Any attempt to alter the actual size of the object za, violates the contracted resource requirements of its definition. Assign the entire Bear object to za and the object overflows its allocated memory. As a result, the executable is literally corrupted, although the corruption may not manifest itself as a core dump.
So, when a base class object is directly initialized or assigned with a derived class object, the derived object is sliced to fit into the available memory resources of the base type. There is nothing of the derived type remaining.

No comments:

Post a Comment