CPSC 427a: Object-Oriented Programming
Michael J. Fischer
Derivation
Class
relationships
Classes relate to and collaborate with other classes.
Many ways in which one class relates to other.
We first explore derivation, where one class modifies and extends another.
What is derivation?
One class can be derived from another.
Syntax:
A is the base class; B is the derived class.
B inherits the members from A.
Instances
A base class instance is contained in each derived class instance.
Similar to composition, except for inheritance.
Function members are also inherited.
Data and function members can be overridden in the derived
class.
Derivation is a powerful tool for allowing variations to a design.
Some uses of derivation
Derivation has several uses.
Example: Parallelogram
Example: Rectangle
New class Rectangle inherits area(), perimeter(), and print() functions from Parallelogram.
Example: Square
New class Square inherits the perimeter(), and print() functions from Parallelogram (via Rectangle).
It overrides the function area().
Notes on Square
Features of Square.
Construction, Initialization, and Destruction
Structure of an object A simple object is like a struct in C.
It consists of a block of storage large enough to contain all of its data
members.
An object of a derived class contains an instance of the base class
followed by the data members of the derived class.
Example:
class B : A { …};
B bObj;
Then “inside” of bObj is an A-instance!
Example of object of a derived class
The declaration A aObj creates a variable of type A and storage size large enough to contain all of A’s data members (plus perhaps some padding).
aObj:
int x;
The declaration B bObj creates a variable of type B and storage size large enough to contain all of A’s data members plus all of B’s data members.
bObj:
int x; int y;
The inner box denotes an A-instance.
Referencing a composed object
Contrast the previous example to
class B { A aObj; …};
B bObj;
Here B composes A.
The embedded A object can be referenced using data member name aObj, e.g., bObj.aObj.
Referencing a base object
How do we reference the base object embedded in a derived class?
Example:
class A { public: int x; int y; …};
class B : A { int y; …};
B bObj;
Initializing an object
Whenever a class object is created, one of its constructors is called.
If not specified otherwise, the default constructor is called.
This is the one that takes no arguments.
If you do not define the default constructor, then the null constructor
(which does nothing) is used.
This applies not only to the “outer” object but also to all of its
embedded objects.
Construction rules
The rule for an object of a simple class is:
The rule for an object of a derived class is:
Destruction rules
When an object is deleted, the destructors are called in the opposite
order.
The rule for an object of a derived class is:
Constructor ctors
Ctors (short for constructor/initializors) allow one to supply parameters
to implicitly-called constructors.
Example:
Initialization ctors
Ctors also can be used to initialze primitive (non-class) variables.
Example:
Multiple ctors are separated by commas.
Ctors present must be in the same order as the construction takes place – base class ctor first, then data member ctors in the same order as their declarations in the class.
Initialization not same as assignment
Previous example using ctors is not the same as writing
B( int n ) { y=n+1; x=n; };
Copy constructors
Polymorphic Derivation
Polymorphism and Type Hierarchies Consider following simple type hierarchy:
We have a base class B and derived classes U and V.
Declare B* bp; U* up = new U; V* vp = new V.
Can write bp = up; or bp = vp;.
Why does this make sense?
*up has an embedded instance of B.
*vp has an embedded instance of B.
Relationships: A U is a B (and more). A V is a B (and more).
Polymorphic pointers Recall:
bp can point to objects of type B, type U, or type V.
Say bp is a polymorphic pointer.
Want bp->f() to refer to U::f() if bp contains a U pointer.
Want bp->f() to refer to V::f() if bp contains a V pointer.
In this example, bp->f() always refers to B::f().
Virtual functions Solution: Polymorphic derivation
A virtual function is dispatched at run time to the class of the actual
object.
bp->f() refers to U::f() if bp points to a U.
bp->f() refers to V::f() if bp points to a V.
bp->f() refers to B::f() if bp points to a B.
Here, the type refers to the allocation type.
Unions
and
type
tags
We can regard bp as a pointer to the union of types B, U and
V.
To know which of B::f(), U::f() or V::f() to use for the call
bp->f() requires runtime type tags.
If a class has virtual functions, the compiler adds a type tag field to each object.
This takes space at run time.
The compiler also generates a vtable to use in dispatching calls on virtual functions.
Virtual destructors
Consider delete bp;, where bp points to a U but has type B*.
The U destructor will not be called unless destructor B::~B() is declared
to be virtual.
Note: The base class destructor is always called, whether or not it is
virtual.
In this way, destructors are different from other member methods.
Conclusion: If a derived class has a non-empty destructor, the base class destructor should be declared virtual.