Upload
alfred-beasley
View
227
Download
7
Tags:
Embed Size (px)
Citation preview
Inheritance – Derived Classes
Employee Inheritance Hierarchy
Employee
HourlyEmp SalariedEmp
derived classes
base class
Derived Classes
Derived classes are used to create a class hierarchy.
The superclass is called the base class.
The subclass is called the derived class.
Friendship is not inherited.
Specifying Inheritance
In the class declaration, we follow
class name
with
:superclass
or
:private superclass
or
:public superclass
Employee Inheritance Example
class Employee
{
public:
Employee( );
Employee(int emp_id, char *emp_name);
Employee(const Employee &e);
~Employee( );
float calculate_pay( );
void set_tax_rate(float new_rate);
void print( );
protected:
float gross_pay;
private:
int id;
char *name;
float tax_rate;
};
Employee Inheritance Example (Continued)
class HourlyEmp : public Employee
{
public:
HourlyEmp( );
HourlyEmp(int emp_id, char *emp_name);
HourlyEmp(int emp_id, char *emp_name, float rate);
HourlyEmp(const HourlyEmp &h);
~HourlyEmp( );
float calculate_pay( );
void set_hours(float worked);
void set_pay_rate(float rate);
void print( );
private:
float hours_worked;
float pay_rate;
};
Member Visibility
Class members are divided into three categories of accessibility:
private member Can be accessed only by the member functions
and
friends of its class
protected member Behaves as a public member to a derived class; it
behaves as a private member to the rest of the
program.
public member Is accessible from anywhere within a program.
Inheritance and Visibility
Private inheritance changes the visibility of the base class members inherited
by the derived class:
private member remains private in the derived class and is not
accessible by the derived class methods
protected member becomes private in the derived class
public member becomes private in the derived class
When public inheritance is used, the visibility of the base class members is not
changed in the derived class
Storage Layout for Class Instances
base classdata
derived classdata
Visibility between Objects of the Same Class
What can an object access within ITSELF and other objects of the SAME
CLASS? [What part of Y can Z access?]
A
B
B Y, Z;
Base class parts(A part of Y)publicprotectedprivate
Derived class parts(B part of Y)publicprotectedprivate
Public inheritance Private inheritance
YYn
YYY
YYn
YYY
Visibility between Base and Derived Class Object
What parts of an object can be accessed by another object of a DERIVED
class? [What part of Y can be accessed by Z?]
A
B
B Z;A Y;
Base class parts(A part of Y)publicprotectedprivate
There is no B part of Y !
Public inheritance Private inheritance
YYn
Ynn
External Visibility
A
B
B Z;
Base class parts(A part of Y)publicprotectedprivate
Derived class parts(B part of Y)publicprotectedprivate
Public inheritance Private inheritance
Ynn
Ynn
nnn
Ynn
What parts of an object can be accessed from OUTSIDE its inheritance
hierarchy? [What part of Z can be accessed from main?]
Visibility of Members Example
class A
{
public:
int a1;
void fa( ) { a1 = a2 = a3 = 0; }
protected:
int a2;
private:
int a3;
};
main( )
{
A x;
x.a1 = 1;
x.a2 = 1;
main( ) cannot access A::a2: protected member
x.a3 = 1;
main( ) cannot access A::a3: private member
}
Visibility of Members Example (Continued)
class B : public A
{
public:
int b1;
void fb( );
protected:
int b2;
private:
int b3;
};
void B::fb( )
{
b1 = b2 = b3 = 0;
a1 = 1;
a2 = 2;
a3 = 3;
B::fb() cannot access A::a3: private member
}
Visibility of Members Example (Continued)
main( )
{
B y;
y.a1 = 2;
y.fa( );
y.a2 = 2;
main( ) cannot access A::a2: protected member
y.a3 = 2;
main( ) cannot access A::a3: private member
y.b1 = 2;
y.b2 = 2;
main( ) cannot access B::b2: protected member
y.b3 = 2;
main( ) cannot access B::b3: private member
}
Visibility of Member Example (Continued)
class C : private A
{
public:
int c1;
void fc( );
protected:
int c2;
private:
int c3;
};
void C::fc( )
{
c1 = c2 = c3 = 0;
a1 = 1;
a2 = 2;
a3 = 3;
C::fc() cannot access A::a3: private member
}
Visibility of Members Example (Continued)
The following statements cause the indicated compiler errors:
main( )
{
C z;
z.a1 = 3; //z.fa(); would be similar
main( ) cannot access a1: A is a private base class
z.a2 = 3;
main() cannot access A::a2: protected member
z.a3 = 3;
main() cannot access A::a3: private member
z.c1 = 3;
z.c2 = 3;
main() cannot access C::c2: protected member
z.c3 = 3;
main() cannot access C::c3: private member
}
Summary of Access Rules for C++
C++ provides function and data protection through a combination of the
following:
public, private, and protected class members
inheritance
friendship
Invoking Parent Class Constructors
In the constructor for a derived class, its parent’s constructor may be passe
d data by putting
: BaseClassName (arguments)
after the function header. For example,
SalariedEmp (int id, char *name) : Employee(id, name) { }
This allows the base class part of the object to be initialized at object constr
uction.
The parent’s constructor is always invoked before the body of the derived
class constructor
Employee Constructor Examples
Employee::Employee( ) : id(0), tax_rate(0)
{
name = strdup(“”);
gross_pay = 0;
}
Employee::Employee (int emp_id, char *emp_name)
{
id = emp_id;
name = strdup(emp_name);
gross_pay = tax_rate = 0;
}
Employee::Employee (const Employee& e)
{
id = e.id;
name = strdup(e.name);
gross_pay = e.gross_pay;
tax_rate = e.tax_rate;
}
Employee Constructor Examples (Continued)
HourlyEmp::HourlyEmp( )
{
hours_worked = pay_rate = 0;
}
HourlyEmp::HourlyEmp(int emp_id, char *emp_name) : Employee(emp_id, emp_name)
{
hours_worked = pay_rate = 0;
}
HourlyEmp::HourlyEmp(int emp_id, char *emp_name, float rate) :Employee(emp_id, emp_name), pay_rate(rate), hours_worked(0)
{ }
HourlyEmp::HourlyEmp(const HourlyEmp& h) : Employee(h)
{
hours_worked = h.hours_worked;
pay_rate = h.pay_rate;
}
Inherited Functions
Member functions inherited from a public base class can be sent to
instances of a derived class.
An instance of a derived class can be passed as a parameter declared to be
an instance of a public base class from which it is derived.
A reference to a derived class can be passed as a parameter declared to be a
reference to an instance of a public base class from which it is derived.
A pointer to a derived class can be passed as a parameter declared to point
to an instance of a public base class from which it is derived.
Inherited Functions (Continued)#include <string.h>
void test_function(Employee);
main( )
{
Employee e1(101, “Chris”), e2;
HourlyEmp h1(102, “Kerry”), h2(103, “Lee”, 25.00);
h2.set_hours(41.0);
h2.set_tax_rate(0.18); // method inherited from Employee
cout << “Pay = “ << h2.calculate_pay() << endl;
test_function(e1);
test_function(e2);
}
void test_function(Employee x)
{
x.print( );
cout << endl;
}
Pointers to Class Instances
A pointer to a base type is allowed to point to an instance of a class derived
from it. This is not true if private inheritance was specified between the bas
e and derived classes.
Reference to a base class may also be assigned an instance of a class derive
d from it
To allow a pointer to a derived class to point to an instance of its superclas
s, it must be explicitly converted with an appropriate constructor or type co
nversion operator.
Pointers to Class Instances (Continued)
main( )
{
Employee e1(101, “Chris”), e2;
HourlyEmp h1(102, “Kerry”), h2(103, “Lee”, 25.00);
Employee *eptr;
HourlyEmp *hptr;
eptr = &e1;
hptr = &h2;
eptr = &h1; // base class ptr points to derived class object
// hptr = &e2; is not allowed
// hptr = eptr; is not allowed
eptr->print( ); // use the pointer to invoke Employee::print
hptr->print( ); // invoke HourlyEmp::print
hptr = (HourlyEmp *) eptr; // ok, but Dangerous!
}
Classes That Allocate Storage
Storage Allocation - New
This statement allocates storage for one object of type int.
int *ptr = new int;
This statement allocates storage for an array of ten objects of type int.
int *ptr = new int[10];
This statement allocates storage for one object of type Myclass. After storage
is allocated, the value 1024 is passed to the constructor and the storage is
initialized.
Myclass *ptr = new Myclass(1024);
Storage Allocation – New (Continued)
new is a unary operator that takes a type as its operand and returns a pointe
r to free storage sufficient to hold an instance of that type.
Vectors of objects may be allocated by using an array specifier in the opera
nd of new.
When new fails, it calls the function pointed to by the pointer _new_handle
r. If no such pointer is found, it returns 0. The pointer can be explicitly set,
or the function set_new_hander can be called.
new can be overloaded.
set_new_handler
#include <new.h>
void noSpace( )
{
cerr << “New Failed” << endl;
exit(1);
}
main( )
{
set_new_handler(noSpace);
…
}
Polygon Class Description
Polygons are geometric figures with one or more sides. They have the
following methods:
A constructor that takes the number of sides and allocates storage for them.
A void constructor that sets the number of sides to 0.
A method to assign the length of the side of a polygon.
A method to compute the perimeter of a polygon.
A method to print the polygon.
A method to compute the area of a polygon.
Polygon Class
class Polygon // implements geometric shapes of 1 or more sides.
{ // uses ints to represent the lengths of the sides.
public:
Polygon( );
Polygon(int n_sides);
void assignSide(int which_side, int len);
int perimeter( );
void print( );
int area( );
protected:
int *sides;
private:
int num_sides;
};
Polygon Class (Continued)
Polygon::Polygon(int n_sides)
{
num_sides = n_sides;
sides = new int [num_sides];
for(int i = 0; i < num_sides; i++)
assignSide(i,0);
}
Polygon::Polygon()
{
num_sides = 0;
sides = NULL;
}
Polygon Class (Continued)
void Polygon::assignSide(int which_side, int len)
{
if(which_side < num_sides)
sides[which_side] = len;
else
cerr << “assignSide: value out of range” << endl;
}
int Polygon::perimeter( )
{
int sum = 0;
for (int i = 0; i < num_sides; i++)
sum += sides[i];
return sum;
}
Polygon Class (Continued)
int Polygon::area( )
{
cerr << “Area undefined for generic polygon” << endl;
return 0;
}
void Polygon::print( )
{
cout << “A polygon with sides : “ << endl << “\t”;
for(int i = 0; i < num_sides; i++)
cout << “ “ << sides [i];
}
Using the Polygon Class
Polygon triangle(3);
main()
{
int side;
for (int i = 0; i < 3; i++)
{
cin >> side;
triangle.assignSide(i, side);
}
cout << triangle.perimeter( );
}
Storage Allocation - Delete
delete is a unary operator that is applied to a pointer returned by new. It ex
plicitly deallocates the storage pointed to by the pointer.
For arrays of user-defined objects, use the following form:
delete [ ] pointer;
This ensures that the destructor is called for each element of the array.
The compiler cannot distinguish a pointer to a vector from a pointer to a si
ngle object.
delete can be overloaded
Polygon Class Destructor
Polygon::~Polygon( )
{
delete sides;
}
Memberwise Assignment Polygons
Polygon pentagon(5), triangle(3);
Polygon aShape;
aShape = triangle;
5
5
5
pentagon
triangle
aShape
Overloading =
Polygon& Polygon::operator = (Polygon& p)
{
if(this != &p) {
int *new_sides = new int[p.num_sides];
for(int i = 0; i < p.num_sides; i++)
new_sides[i] = p.sides[i];
delete sides;
sides = new_sides;
num_sides = p.num_sides;
}
return (*this);
}
Assignment versus Initialization
Assignment and initialization are not the same.
A user-defined assignment operator is not applied to an uninitialized object.
Initialization (the copy constructor) is used when class instances are initiali
zed in declarations, for passing instances as function arguments, and for ret
urning instances from functions.
The Copy Constructor
Polygon::Polygon(const Polygon& p)
{
num_sides = p.num_sides;
sides = new int [num_sides];
for (int i = 0; i < num_sides; i++)
sides[i] = p.sides[i];
}
Example of Constructor Invocation
Polygon test(Polygon p) // passing a to test: copy constructor
{
return p; // return Polygon from test: copy constructor
}
main()
{
Polygon a(5); // 1-parameter constructor
Polygon b = Polygon(3); // 1-parameter constructor (Polygon b(3))
Polygon c = a; // copy constructor
Polygon d; // void constructor
d = test(a); // assign result of test to d: assignment operator
// destruct function argument: destructor
// destruct temporary function result: destructor
} // destruct d, c, b, a : destructor
Classes Allocating Storage
When a class dynamically allocates storage, each of the following is necess
ary in the class:
regular constructor(s)
destructor
overloaded assignment operator
copy constructor
Default Memberwise Copy
If class does not provide an overloaded assignment operator or copy constr
uctor, when assignment or copying of instances is done, each base class an
d member class object in it has
its assignment operator or copy constructor invoked, if it exists
memberwise copy applied otherwise
Default Memberwise Copy (Continued)
If a class does provide an overloaded assignment operator or copy
constructor, it is invoked when assignment or copying of instances is done.
It is the responsibility of the class operators to invoke the base class or
member class operators, or both, as necessary.
If both a derived class and a base class define assignment operators and a
derived class object is assigned to a base class object, the type of the left-
hand operand determines the assignment operator used.
Virtual Functions
Polygon Class
class Polygon // implements geometric shapes of 1 or more sides.
{ // uses ints to represent the lengths of the sides.
public:
Polygon( );
Polygon(int n_sides);
void assignSide(int which_side, int len);
int perimeter( );
void print( );
int area( );
protected:
int *sides;
private:
int num_sides;
};
A Polygon Hierarchy
Polygon
Triangle Rectangle
Square
Using the Polygon Classes
Polygon p1(5);
Triangle t1, t2(10, 10, 10);
Rectangle r1, r2(10, 20);
Square s1, s2(10);
p1.assignSide(0, 99);
cout << t2.perimeter() << endl;
t2.print(); cout << endl;
cout << r2.area() << endl;
r2.print(); cout << endl;
cout << s2.area() << endl;
s2.print(); cout << endl;
Accessing Polygons through Pointers
Polygon *p[50];
Triangle t1, t2(10, 10, 10);
Rectangle r1, r2(10, 20);
Square s1, s2(10);
p[0] = &t1;
p[1] = &r2;
p[2] = &s2;
…
cout << p[i]->area() << endl;
p[i]->print();
Accessing Polygons through Pointers (Continued)
Polygon
Triangle Rectangle
Square
The code for print() and area()is found in this class
Virtual Functions
If we change the declaration of the function area in Polygon to
virtual int area( );
And the declaration of function print in Polygon, to
virtual void print( );
Then print and area are dynamically bound to the code, appropriate to the
object to which they are sent.
Virtual Function (Continued)
Polygon
Triangle Rectangle
Square
The code for p[1]->print( ), andp[1]->area( ), is found in this class
The code for p[0]->print( ), andp[0]->area( ), is found in this class
The code for p[2]->print( ), andp[2]->area( ), is found in this class
Virtual Function (Continued)
The keyword virtual is specified once for the inheritance hierarchy by placi
ng it in the root of the tree or subtree. It is used to dynamically bind a mem
ber function to the appropriate code, based on the type of the receiver.
The derived classes can redefine the virtual function or inherit it. They can
also define additional virtual functions.
The access level (public, private, or protected) is specified by the type thro
ugh which it is invoked.
Pure Virtual Functions
In a base class, the virtual function declaration can be specified as follows:
virtual retType name(arguments) = 0;
This function is called a pure virtual function. The class declaring it is calle
d an abstract class.
It is illegal to create instances of classes containing one or more pure virtua
l function declarations.
A pure virtual function that is not redefined in a derived class will be inheri
ted as a pure virtual function, and the derived class will become an abstract
class.
Virtual Destructors
Given pointers to a base class (which may point to instances of the derived
classes), if we delete them, we invoke the destructor for the base class. To
overcome this, we use virtual destructors.
To declare a destructor as virtual, precede its declaration with the keyword
virtual, in the base class.
Even though destructors in a derived class don’t have the same name as in
the base class, they can still be declared virtual.