Exercises

* 11.2, 11.3, 11.4, 11.5.

Reading Assignments

* Ch 11.4 (skip type casting and variant records)

Final Exam

* Fri, Aug 18 (1:10 - 3:00).

* 110 minutes for 100 points (25% of your grade).

Office Hour Changes This Week

* No office hour on Friday (8/11).

* Extended office hour on Wednesday (8/9) from 2:20 to 4:30pm.

Object-oriented programming (OOP)

* Concepts

1. An "object" (a class instance) is a self-contained entity encapsulating "data" and "operations" on that data.

2. An object has an internal "state" (maintained by its data members) and a set of "methods" (public member functions), the only means by which the object's state can be inspected or modified by another object.

3. An OO program comprises a collection of objects, communicating with one another by "message passing" (member function calls).

* In addition to facilities for creating ADTs, most researchers agree that true OO languages (e.g. Smalltalk) require two features not usually found in non-OO languages (e.g. Fortran):

1. Inheritance

2. Dynamic binding

Inheritance

* Classifying concepts by arranging them into (inheritance) hierarchies often helps to illuminate the similarities and differences among these concepts.

A bigger example

Inheritance

* Single inheritance - each derived class inherits properties from only one ancestor (base class).

* Multiple inheritance - very easy to misuse and is out of scope of this class.

* A derived class in C++ is often called a subclass in other OOP languages.

* A base class in C++ is often called a superclass in other OOP languages.

* In many cases, D is a derived class of B means that D "is-a" B.

* Thus a class derivation usually represents the "is-a" relationship between classes.

The good old Jar_Type class

* Many weeks ago, we implemented a Jar_Type class.

class Jar_Type {

public:

void add(int in_n);

int get_size() const;

Jar_Type(int in_n = 0);

private:

int units;

};

* Suppose now I need to add a label on a jar as well as methods (usually member functions) to write the label on the jar, and to print the label, etc.

Without using inheritance

class Labeled_Jar {

public:

Labeled_Jar(const char* in_label, int in_n = 0);

void add(int in_n);

int get_size() const;

void write_label(ostream& ost) const;

private: char label[20];

Jar_Type jar; // reusing Jar_Type

}; // as a component

void Labeled_Jar::add(int in_n)

{ jar.add(in_n); }

int Labeled_Jar::get_size() const

{ return jar.get_size(); }

Using inheritance (Deriving a C++ class)

class Labeled_Jar : public Jar_Type {

public:

Labeled_Jar(const char* in_label, int in_n = 0);

void write_label(ostream& ost) const;

private:

char label[20];

};

// client's code

Labeled_Jar jar1("jelly beans", 60);

jar1.add(20);

Labeled_Jar jar2("M & M");

jar2.add(100);

cout << jar1.get_size() << " " << jar2.get_size();

...

Class derivation

* "public" derivation means that all public members of Jar_Type (except constructors and destructors) are also public members of Labeled_Jar.

* i.e., Jar_Type's add() and get_size() functions are also public members of Labeled_Jar.

* Note the public part of Labeled_Jar specializes the base class by adding a new function write_label() and declaring its own constructor.

* Although a derived class inherits the members of its base class, both private and public, it can access only the public members of the base class.

Implementation of Labeled_Jar

* suppose we want to write the label as well as the size...

Labeled_Jar::write_label(ostream& ost) const

{

ost << "jar " << label << " has " << units

<< "units.\n";

}

* What is wrong with that code? Why?

* How would you fix it?

Constructors & Destructors

* A base class's constructor is implicitly called first, before the derived class's constructor is executed.

* However, the derived class's destructor is executed first, followed by the base class's destructor.

* In other words, C++ class objects are constructed inside-out and destroyed outside-in.

* In other words, all sub-objects of an object are created before the rest of the object is created, and destruction occurs in the opposite order.

Writing constructors for derived classes

Labeled_Jar::Labeled_Jar(const char* in_label,

int in_n)

: Jar(in_n)

{

strcpy(label, in_label);

}

* Initializers are used to initialize the base class's objects.

* You may also initialize a data member that way, e.g.

Jar_Type::Jar_Type(int n)

: units(in_n)

{ }

C++ supplies the following for a class

1. a default constructor - ONLY if you haven't written ANY constructor.

2. a copy constructor - if you haven't written one.

3. an assignment operator - if you haven't written one.

4. a destructor - if you haven't written one.

* If a base class has its own versions of some of the functions listed above, you may not want to rely on the functions supplied by C++.

* In fact, you may want to supply your own functions (even if empty) just to ensure that the corresponding member functions of the base class are called.

Important notes

* If you forget to use "public" to inherit, i.e.,

class Derived : Base {

// ...

};

* then "private" inheritance is assumed.

* Private inheritance hides the base class's public members from the public.

* It is easy to misuse private inheritance.

* Private inheritance is beyond the scope of this class.

Overriding inherited member functions

class Labeled_Jar : public Jar_Type {

public:

// ...

void add(int in_n); // overriding a function!

private:

char label[20];

};

* You may override an inherited member function by

1. declare the same function prototype, i.e., (same return type, same function name, same argument types).

2. define the function in your implementation.

Labeled_Jar::add(int in_n)

{ units += in_n * 2; } // A majic jar that duplicates

Exercise

main()

{

Labeled_Jar jar1("magic jelly beans");

jar1.add(20);

cout << jar1.get_size() << endl;

Jar jar2("jelly beans");

jar2.add(20);

cout << jar2.get_size() << endl;

// ...

}

Type compatibilities

* A pointer to a derived class object may be converted to a pointer to an object of its base class.

* The converse is not true.

* Similarly, a reference to a derived class object may be converted to a reference to a base class object.

* Similarly, the converse is not true.

Exercises

Jar_Type jar1(...);

Labeled_Jar jar2(...);

Jar_Type jar_p = new Jar_Type(...);

Labeled_Jar jar_q = new Labeled_Jar(...);

// which of the following is incorrect?

// what kind of errors (compilation/runtime) would

// you get in each case?

Jar_Type* a = jar1; // error

Jar_Type* b = &jar1; // okay

Jar_Type* c = jar_p; // okay

Jar_Type* d = jar2; // error

Jar_Type* e = &jar2; // okay

Jar_Type* f = jar_q; // okay

Labeled_Jar* g = jar1; // error

Labeled_Jar* h = &jar1; // error

Labeled_Jar* i = jar_p; // error

Labeled_Jar* j = jar2; // error

Labeled_Jar* k = &jar2; // okay

Labeled_Jar* l = jar_q; // okay

Jar_Type* m = jar_p;

m->add(20);

cout << m->get_size() << endl;

// answer is 20, assume jar_p->units starts with 0.

Jar_Type* n = jar_q;

m->add(20);

cout << m->get_size() << endl;

// answer is 20, assume jar_q->units starts with 0.

// the magic add function is NOT invoked!