Chapter 2: Object-Oriented Design

2.1 Goals and Principles

2.1.1 Object-Oriented Design Goals

2.1.2 Object-Oriented Design Principles


2.2 Inheritance and Polymorphism

2.2.1 Inheritance in C++

Inheritance allows the design of generic classes that can be specialized to more particular classes, with the specialized classes reusing the code from the generic class.
class Person {       // base class  
private:
string name; // name
string ssn; // social security number
public:
//...
void print(); // print data
string getName(); // retrieve name
};
class Student : public Person { // derived class
private:
string major; // major subject
int gradYear; // graduate year
public:
//...
void print(); // print data
void changeMajor(string newMajor); // change major
 };
Member Functions
Person person(...);       // define a person 
Student student(...); // define a student
cout << student.getName();// invokes Person::getName()
person.print(); // invokes Person::print();
student.print(); // invokes Student::print();
person.changeMajor("Math"); // ERROR!
student.changeMajor("Math"); // OK
void Person::print() // definition of Person print
{ cout << name << " " << ssn; }
void Student::print() // definition of Student print
{ Person::print(); // first print Person data
cout << major << gradYear;
}

Illustrating Class Protection

class Base {
private:
int priv;
protected:
int prot;
public:
int publ;
};
class Derived: public Base {
void someMemberFunction()
{ cout << priv; // ERROR: private member
cout << prot; // OK
cout << publ; // OK
}
};
class Unrelated {
Base X;
void anotherMemberFunction()
{ cout << X.priv; // ERROR: private member
cout << X.prot; // ERROR: protected member
cout << X.publ; // OK
}
};

Constructors and Destructors

Person::Person(const string &nm, const string &ss)  
: name(nm), ssn(ss) {} // initializer list
Student::Student(const string &nm, const string &ss,
const string &maj, int year)
: Person(nm, ss), // initialize Person data members
major(maj), // initialize member
gradYear(year) {} // initialize gradYear
Person::~Person() // Person destructor
{ ... }
Student::~Student() // Student destructor
{ ... }
Student* s = new Student(...);
//...
delete s; // calls ~Student() then ~Person()
    [person.cpp]

Static Binding

When determining which member function to call, C++ default action is to consider an object's declared type, not its actual content.
Person* pp[100];  
pp[0] = new Person(...);
pp[1] = new Student(...);

cout << pp[1]->getName(); // OK
pp[0]->print(); // calls Person::print()
pp[1]->print(); // calls Person::print()
pp[1]->changeMajor(...); // ERROR!

Dynamic Binding and Virtual Functions

In dynamic binding, an object's contents determine which member function is called.
class Person {       // base class  
virtual void print(); // print data
//...
};
class Student : public Person { // derived class
virtual void print(); // print data
//...
 };
Person* pp[100];
pp[0] = new Person(...);
pp[1] = new Student(...);

pp[0]->print(); // calls Person::print()
pp[1]->print(); // calls Student::print()
pp[1]->changeMajor(...); // OK
    [person.cpp]

Virtual Destructors

If a class defines any virtual functions, it should define a virtual destructor, even if it is empty.
delete [] pp;
When to use virtual destructors?
In C++, what’s a virtual destructor and when is it needed?

2.2.2 Polymorphism

The ability of a variable to take different types!


2.3 Templates

2.3.1 Function Templates

2.3.2 Class Templates

template <typename Object>
class BasicVector {
Object* a; // array storing an element
int capacity; // length of array a
public:
BasicVector(int cap = 10) // constructor
{ capacity = cap;
a = new Object[capacity];// allocate array storage
}
Object& elemAtRank(int r) // access element at index r
{ return a[r]; }
// ...
};
BasicVector<int> iv(5);     // vector of 5 integers
BasicVector<double> dv(20); // vector of 20 doubles
BasicVector<string> sv(10); // vector of 10 strings
//...
iv.elemAtRank(3) = 8; // iv[3] = 8;
dv.elemAtRank(14) = 2.5; // dv[14] = 2.5
sv.elemAtRank(7) = "hello"; // sv[7] = "hello"


2.4 Exceptions

Exceptions are unexpected events that occur during the execution of a program.

2.4.1 Exception Objects

Using Inheritance to Define New Exception Types

class MathException {  // generic math exception
private:
string errMsg; // error message
public:
MathException(const string& err) { errMsg = err; }
};
class ZeroDivisionException : public MathException {
public:
ZeroDivisionException(const string& err)
: MathException(err) {}
};
class NegativeRootException : public MathException {
public:
NegativeRootException(const string& err)
: MathException(err) {}
};

2.4.2 Throwing and Catching Exceptions

try {
// ...
if (divisor == 0) throw ZeroDivisionException("Division by zero");
//...
}
catch (ZeroDivisionException& zde)
{ // handle division by zero
}
catch (MathException& me)
{ // handle any math exception other than division by zero
}
Once execution of the catch block completes, control flow continues with the first statement after the last catch block.

2.4.3 Exception Specification

void calculator() throw(ZeroDivisionException, NegativeRootException)
{
//...
}

void funct1(); // can throw any exception
void funct2() throw(); // can throw no exception

Generic Exception Class

class RuntimeException {  // generic run-time exception
private:
string errorMsg;
public:
RuntimeException(const string& err) { errorMsg = err; }
string getMessage() const { return errorMsg; }
};
inline std::ostream& operator<<(std::ostream& out, const RuntimeException& e)
{ return out << e.getMessage(); }