Object-oriented programming constructs

Object orientation has brought about a radical shift in our notion of computation and how we look at programming. This chapter introduces the basic mechanisms of object-oriented programming.


Object-oriented programming constructs

2

Additional keywords and phrases: {\em computing device, C++, generic types, canonical classes, pointers, references, virtual functions, extensions to the object model }

The object model

  • computation is sending messages between objects

Message

  • object method arguments

Encapsulation

  • objects encapsulate data and procedures

Protocol

  • the collection of messages an object supports

  class counter {
  int n;
  public:
  
  counter() { n = 0; }
  
  void operator++() { n = n + 1; }
  int value() { return n; }
  };
  

  class counter {
  public:
  
  counter(int v = 0 ) : n(v) { init("default"); }
  counter(char* s, int v=0) : n(v) { init(s); } 
  ~counter() { delete[] id; }
  	
  char* name() { return id; }
  
  void operator++() { n = n + 1; }
  int value() { return n; }
  private:
  int n; char* id;
  void init(char* s) { 
  	id = new char[strlen(s)+1];
  	strcpy(id, s);
  	}
  };
  
  

  class counter {
  public:
  
  counter(int v = 0 );
  counter( char* s, int v = 0 ); 
  
  ~counter() { delete[] id; }
  	
  const char* name() const { return id; }
  
  void operator++() { n = n + 1; }
  int value() const { return n; }
  
  private:
  int n; char* id;
  void init(char* s);
  };
  

  class counter {
  public:
  
  counter(int v = 0 ) : n(v), id("default") { }
  counter( char* s, int v = 0 );
  	
  ~counter() { delete[] id; }
  	
  const char* name() { return id; }
  
  void operator++() { n = n + 1; }
  
  operator int() { return n; }
  operator char*() { return id; }
  
  private:
  int n; char* id;
  };
  

  class counter {
  friend int operator<(counter&, int);
  public:
  
  counter(int v = 0 );
  counter( char* s, int v = 0 );
  	
  ~counter() { delete[] id; }
  	
  const char* name() { return id; }
  int value() const { return n; }
  
  void operator++() { n = n + 1; }
  
  private: int n; char* id;
  void init(char* s);
  };
  
  int operator<(counter& c, int i) { return c.n < i; }
  

  class shape { 
\fbox{shape}
public: shape(int x = 0, int y = 0) : _x(x), _y(y) { } void move(int x, int y ) { _x += x; _y += y; } virtual void draw() = 0;
// pure virtual
protected: int _x, _y; };

  class circle : public shape {  
\fbox{circle}
public: circle( int x, int y, int r) : shape(x,y), _radius(r) { } void draw() { cout << "C:" << _x << _y << _radius; } protected: int _radius; }; class rectangle : public shape {
\fbox{rectangle}
public: rectangle( int x, int y, int l, int r ) : shape(x,y), _l(l), _r(r) { } void draw() { cout << "R:" << _x << _y << _l << _r; } protected: int _l,_r; };

  circle c(1,1,2); rectangle r(2,2,1,1);
  
  c.draw(); r.draw();
  

  
  
  
class compound : public shape { 
\ifsli{}{\fbox{compound}}
public: compound( shape* s = 0 ) : fig(s) { next = 0; } void add( shape* s ) { if (next) next->add(s); else next = new compound(s); } void move(int x, int y) { if (fig) fig->move(x,y); if (next) next->move(x,y); } void draw() { if (fig) fig->draw(); if (next) next->draw(); } private: shape* fig; compound* next; };

class student { ... };
  class assistant { ... };
  
  class student_assistant
  		: public student, public assistant {
  public:
  student_assistant( int id, int sal ) 
  		: student(id), assistant(sal) {}
  };
  

class person { };
  class student : virtual public person { ... }
  class assistant : virtual public person { ... }
  
  class student_assistant
  	: public student, public assistant { ... };
  

  double sqrt( double arg ) { 
\fbox{sqrt}
require ( arg >= 0 ); double r=arg, x=1, eps=0.0001; while( fabs(r - x) > eps ) { r=x; x=r-((r*r-arg)/(2*r)); } promise ( r - arg * arg <= eps ); return r; }

class counter { 
\fbox{counter}
public: counter(int n = 0) : _n(n) { require( n >= 0 ); promise( invariant() );
\c{// check initial state}
} virtual void operator++() { require( true );
\c{// empty pre-condition}
hold();
\c{// save the previous state}
_n += 1; promise( _n == old_n + 1 && invariant() ); } int value() const { return _n; }
\c{// no side effects}
virtual bool invariant() { return value() >= 0; } protected: int _n; int old_n; virtual void hold() { old_n = n; } };

class bounded : public counter { 
\fbox{bounded}
public: bounded(int b = MAXINT) : counter(0), max(b) {} void operator++() { require( value() < max() );
\c{// to prevent overflow}
counter::operator++(); } bool invariant() { return value() <= max && counter::invariant(); } private: int max; };

Canonical class

  • default constructor
  • copy constructor
  • destructor
  • assignment
  • operators
Abstract data types must be indistinguishable from built-in types

String class

canonical

  class string {
  public:
  
  string(char* s="") { init(s); }
  string(string& a) { init((char*)a); } 
  ~string() { delete p; }
  
  string& operator=( string& a ) {
     init((char*)a); return *this;
     } 
  
  string operator+( string& a );
  
  int length() { return strlen(p); } 
  
  operator char*() { return p; }
  private:
  void init(char* s) { 
    p = new char[strlen(s)+1]; strcpy(p,s);
    }
  char* p;
  };
  

String handler

envelope

  class string {
  public:
  
  string(char* s = "") { rep = new stringrep(s); }
  string(string& a) { rep = a.rep; rep->count++; } 
  string& operator=( string& a ) { 
  	   a.rep->count++;
  	   if (--rep->count <= 0 ) delete rep;
  	   rep = a.rep;
  	   return *this;
  	   }
  
  string operator+( string& a );
  
  int length() { return strlen(rep->rep); } 
  
  operator char*() { return rep->rep; }
  private:
  stringrep* rep;
  };
  

String body

letter

  class stringrep {
  friend string;
  private:
  stringrep(char* s) {
  	rep = new char[strlen(s)+1]; strcpy(rep,s);
  	count = 1;
  	}
  ~stringrep() { delete[] rep; }
  char* rep;
  int count;
  };
  

Generic types

  • polymorphic types -- pointers, void*
  • parametrized types -- template classes
Generic types are essential for programming in the large

Class hierarchies

  class shapelist { 
\fbox{shapelist}
public: shapelist(shape* el=0, shapelist* sl=0) : hd(el), tl(sl) { } shapelist* insert(shape* el) { require( el );
el must exist
if (!hd) hd = el; else return new shapelist(el,this); } shape* head() { return hd; } shapelist* tail() { return tl; } private: shape* hd; shapelist* tl; }

  typedef void* type;  
generic void*
class stack {
\fbox{stack}
public: stack( int n = 12 ) { top = -1; impl = new type[n]; } ~stack() { delete[] impl; } bool empty() { return top == -1; } void push( type it ) { impl[++top] = it; } type pop() { return impl[top--]; } private: int top; type* impl; };

  stack s(100);
  char plus = '+'; char c = 'c';
  s.push(&plus); s.push(&plus); s.push(&c);
  while ( !s.empty() ) {
    		cout << *(char*) s.pop();
    		}
  

  template<class type>
  
  class stack  { 
\fbox{stack}
public: stack( int n ) { top = -1; impl = new type[n]; } ~stack() { delete[] impl; } bool empty() { return top == -1; } void push( type it ) { impl[++top] = it; } type pop() { return impl[top--]; } private: int top; type* impl; };

@s lib/cell.s
  template< class E > 
\fbox{list<E>}
class list { friend class listiter<E>; public: list() { c = 0; } ~list() { if(c) delete c; } void insert(const E& el); operator iter<E>() { return listiter<E>(c); }
(*)
private: cell* c; }; template< class E >
\c{\fbox{list<E>::insert}}
void list<E>::insert(const E& el) { void* x = (void*) ⪙ if (!c) c = new cell(x); else c->insert(x); }

  template< class E >  
\fbox{iter}
class iter { public: iter(iter* x) : it(x) {} virtual E* operator()() { return (*it)(); }
\c{// indirect}
private: iter<E>* it; };

template< class E >  
\fbox{listiter}
class listiter : public iter<E> { public: listiter( cell* c ) : iter<E>(this), p(c) {}
virtuality
~listiter() { cout << "~listiter" << endl; } E* operator()();
the iterator function
private: cell* p; };

The operator() function

template< class E > 
\fbox{listiter::operator()}
E* listiter<E>::operator()() { void* x = p?p->el:0; if (p) p = p->next; return (E*) x;
conversion to (E*)
}

OOP = encapsulation + inheritance

  • How safe are changes in the inheritance hierarchy ?
Encapsulation -- dependencies (contracts) \n Data-abstraction -- behavior (operations)\n Objects -- external interface (escapes)

Inheritance {\rm - new category of clients}

  • What external interface is provided to children ?

class A {
  public:
  A() { s = "XAX"; }
  void print() { cout << s; }
  private:
  char* s;
  };
  
  class B : private A {   }
  

  class sneaky { 
\fbox{sneaky}
private: int safe; public: sneaky() { safe = 12; } int& sorry() { return safe; } int value() { return safe; } };

Cost of inheritance

  • Execution speed: often a misplaced concern
  • Program size: memory cost decreases, optimization
  • Message-passing overhead: {\it reduction as in C++}
  • Program complexity: {\it yo-yo problem: up and down the inheritance graph}

class A { 
\fbox{A}
public: A() { cout << "A"; } ~A() { cout << "A"; } }; class B : public A {
\fbox{B}
public: B() { cout << "B"; } ~B() { cout << "B"; } };

class A { 
\fbox{A}
public: A() { cout << "A"; } virtual ~A() { cout << "A"; } };

class C: public A { 
\fbox{C}
public: C() { cout << "C"; } ~C() { cout << "C"; } }; class D : public B, public C {
\fbox{D}
public: D() { cout << "D"; } ~D() { cout << "D"; } };

class B: virtual public A { 
\fbox{B}
public: B() { cout << "B"; } ~B() { cout << "B"; } }; class C: virtual public A {
\fbox{C}
public: C() { cout << "C"; } ~C() { cout << "C"; } };

Meta classes

  • class wide values, runtime type information

Object identity

  • unique references, distribution

Persistence

  • storage of objects (OODBMS)

Active objects

  • autonomous behavior, concurrency

Abnormal events

  • exceptions -- to indicate failure
  • intervention -- invocation of a correction routine

Exceptions in C++

\zline{\fbox{\em try \& catch}}
  class Matherr { }
  class Overflow : public Matherr {}
  
  try {
  	f(); 
do some arithmetic
} catch (Overflow) {
// handle Overflow
} catch (Matherr) {
// handle non-Overflow Matherr
}

Assertions

\zline{\fbox{\em throw}}
  template< class T, class X >
  inline void Assert(T expr, X x) {
     if (!NDEBUG) if (!expr) throw x;
     }
  

The object model

1

  • object model -- encapsulation, message protocols
  • complexity -- what is the contribution of object orientation

Generic types

4

  • polymorphic types -- due to inheritance
  • parametrized types -- corresponding to template classes

Extensions to the object model

6

  • meta classes -- dynamic type information
  • distribution -- persistence, active objects
  • abnormal events -- exception handling

Questions

  1. Explain the meaning of the phrase "object orientation reduces the complexity of programming."
  2. Explain the role of constructors. What role do destructors play?
  3. What is the meaning of const? Give some examples.
  4. Characterize the two kinds of type conversions supported by C++.
  5. Why do you need friends?
  6. What is a canonical class? Characterize its ingredients and give an example.
  7. Explain the handler/body idiom. Give an example.
  8. What are generic types? Why are they useful? Explain how C++ supports generic types. Give an example.
  9. Explain how inheritance may jeopardize encapsulation. Can you think of a solution?
  10. Give an example of a class allowing external clients access to private data.
  11. Discuss the advantages and disadvantages of inheritance.
  12. What extensions to the classical object model can you think of? Why are these extensions needed?