Sunteți pe pagina 1din 21

Describing Objects Using ADTs

Object-Oriented Design and  An abstract data type (ADT) is a set of


Programming objects and an associated set of opera-
tions on those objects
 ADTs support abstraction, encapsulation,
C++ Language Support for and information hiding
Abstract Data Types { Basically, enhance representational independence :::

 They provide equal attention to data and


Douglas C. Schmidt operations
www.cs.wustl.edu/schmidt/
schmidt@cs.wustl.edu  Common examples of ADTs:
Washington University, St. Louis { Built-in types: boolean, integer, real, arrays
{ User-de ned types: stacks, queues, trees,
lists
1 2

Built-in ADTs User-de ned ADTs


 boolean  stack
{ Values: TRUE and FALSE { Values: Stack elements, i.e., stack of X :::

{ Operations: and, or, not, nand, etc. { Operations: create, dispose, push, pop,
is empty, is full, etc.

 integer
 queue
{ Values: Whole numbers between MIN and MAX
values { Values: Queue elements, i.e., queue of X :::

{ Operations: add, subtract, multiply, divide, { Operations: create, dispose, enqueue, dequeue,
etc. is empty, is full, etc.

 arrays  tree search structure


{ Values: Homogeneous elements, i.e., array of { Values: Tree elements, i.e., tree of X
X:::

{ Operations: insert,delete, find, size,


{ Operations: initialize, store, retrieve, traverse (in-order, post-order, pre-order,
copy, etc. level-order), etc.

3 4
Avoiding Over-Speci cation Over-Speci cation Examples
 Goal:  e.g.,
{ We want complete, precise, and unambigu- int bu er[100], last = ,1;
ous descriptions and speci cations of software
components :::
bu er[++last] = 13;
 Problem:  e.g.,
{ We do not want to be dependent on physical struct Node f
representation int item ;
Node *next ;
 Too hard to port g *p, * rst = 0;
:::

 Too hard to change implementation p = new Node;


p->next = rst; p->item = 13; rst = p;
 Solution  e.g.,
{ Use ADTs template <class T, int SIZE>
class Stack f
 ADTs capture essential properties without public:
over-specifying their internal realizations int push (T new item); /* ::: */
//
private:
:::

 ADT interfaces provide a list of operations T stack [SIZE]


rather than an implementation description g;
 i.e., what rather than how Stack<int, 100> int stack;
// :::
int stack.push (13);
5 6

Algebraic Speci cation of ADTs Algebraic Speci cation of ADTs


(cont'd)
 Allows complete, precise, and non-ambiguous
speci cation of ADTs without over-specifying  Algebraic speci cations attempt to be com-
their underlying implementation plete, consistent, and handle errors
{ e.g., language independent { They consist of four parts: types, functions,
preconditions/postconditions, and axioms
 ADT speci cation techniques must de ne:  e.g.,
types
{ Syntax STACK[T]
functions
 e.g., map function: arguments ! results create: ! STACK[T]
push: STACK[T]  T ! STACK[T]
pop: STACK[T] ! STACK[T]
{ Semantics top: STACK[T] ! T
empty: STACK[T] ! BOOLEAN
 Meaning of the mapping full: STACK[T] ! BOOLEAN
preconditions/postconditions
pre pop (s: STACK[T]) = (not empty (s))
 Often entails preconditions, postconditions, pre top (s: STACK[T]) = (not empty (s))
axioms pre push (s: STACK[T], i: T) = (not full (s))
post push (s: STACK[T], i : T) = (not empty (s)
axioms
{ Exceptions for all t: T, s: STACK[T]:
empty (create ())
 Error conditions not empty (push (t, s))
top (push (s, t)) = t
pop (push (s, t)) = s
7 8
Ei el Stack Example Ei el Stack Example (cont'd)
 -- Implement a bounded stack abstraction  e.g.,
in Ei el
class STACK[T] export pop: T is
is empty, is full, push, pop, top require
feature not is empty
bu er : ARRAY[T]; do
top : INTEGER; Result := bu er.entry (top );
Create (n : INTEGER) is top := top , 1;
do ensure
top := 0; not is full;
bu er.Create (1, n); top = old top , 1;
end; -- Create end; -- pop
is empty: BOOLEAN is push (x : T) is
do require
Result := top <= 0; not is full;
end; -- is empty do
is full: BOOLEAN is top := top + 1;
do bu er.enter (top , x);
Result := top >= bu er.size; ensure
end; -- is full not is empty; top = x;
top: T is top = old top + 1;
require end; -- push
not is empty invariant
do top >= 0 and top < bu er.size;
Result := bu er.entry (top ); end; -- class STACK
end; -- pop
9 10

Ei el Stack Example (cont'd)


 e.g., An Ei el program used to reverse a
name
C++ Support for ADTs
class main feature  C++ Classes
MAX NAME LEN : INTEGER is 80;
MAX STACK SIZE : INTEGER is 80;
Create is
local
io : STD FILES;  Automatic Initialization and Termination
st : STACK[CHARACTER];
str : STRING;
index : INTEGER;
do  Assignment and Initialization
io.create; str.create (MAX NAME LEN);
st.create (MAX STACK SIZE);
io.output.putstring ("enter your name..: ");
io.input.readstring (MAX NAME LEN);
str := io.input.laststring;  Parameterized Types
from index := 1;
until index > str.length or st.is full
loop
st.push (str.entry (index));
index := index + 1;  Exception Handling
end;
from until st.is empty loop
io.output.putchar (st.pop);
 Iterators
end;
io.output.new line;
end;
end;
11 12
C++ Classes C++ Classes (cont'd)
 A C++ class is an extension to the struct  Each access control section is optional,
type speci er in C repeatable, and sections may occur in any
order
 Classes are containers for state variables  Note, access control section order may af-
and provide operations (i.e., methods) fect storage layout for classes and structs:
for manipulating the state variables { C++ only guarantees that consecutive elds
appear at ascending addresses within a section,
not between sections, e.g.,
 A class is separated into three access con-
trol sections: class Foo f /* Compiler may not rearrange these! */
int a ;
class Classic Example f char b ;
public: double c ;
char d ;
// Data and methods accessible to oat e ;
// any user of the class short f ;
protected: g;
// Data and methods accessible to class Foo f /* Compile may rearrange these! */
// class methods, derived classes, and public: int a ;
// friends only public: char b ;
public: double c ;
private: public: char d ;
// Data and methods accessible to class public: oat e ;
// methods and friends only public: short f ;
g; g;
13 14

C++ Classes (cont'd) C++ Class Components


 By default, all class members are private  Nested classes, structs, unions, and enu-
and all struct members are public merated types
{ A struct is interpreted as a class with all data { Versions of AT&T cfront translator later than
objects and methods declared in the public sec- 2.1 enforce proper class nesting semantics
tion

 Data Members
 A class de nition does not allocate stor-
age for any objects { Including both built-in types and user-de ned
class objects
{ i.e., it is just a cookie cutter :::

{ Remember this when we talk about nested classes :::


 Methods
{ Note, a class with virtual methods will allocate { Also called \member functions," only these op-
at least one vtable to store virtual method def- erations (and friends) may access private class
initions data and operations

15 16
Nested Classes et al.
 Earlier releases of C++ (i.e., cfront ver-
sions pre,2.1) did not support nested se-
mantics of nested classes
C++ Class Components (cont'd) { i.e., nesting was only a syntactic convenience

 The this pointer


 This was a problem since it prevented con-
{ Used in the source code to refer to a pointer trol over name space pollution of type names
to the object for which the method is called
{ Compare with static for functions and vari-
ables
 Friends
{ Non-class functions granted privileges to ac-  It is now possible to fully nest classes and
cess internal class information, typically for ef- structs
ciency reasons
{ Class visibility is subject to normal access control :::

 Note, the new C++ namespace feature is


a more general solution to this problem :::

17 18

Nested Classes et al. (cont'd)


Class Data Members
 e.g.,
class Outer f  Data members may be objects of built-in
public: types, as well as user-de ned types, e.g.,
class Visible Inner f /* */ g; class Bounded Stack
private:
:::

class Hidden Inner f /* */ g;


::: #include "Vector.h"
g; template <class T>
class Bounded Stack f
Outer outer; /* OK */ public:
Hidden Inner hi; /* ERROR */ Bounded Stack (int len): stack (len), top (0) fg
Visible Inner vi; /* ERROR */ void push (T new item) f
Outer::Visible Inner ovi; /* OK */ this->stack [this->top ++] = new item;
Outer::Hidden Inner ohi; /* ERROR */ g
T pop (void) f return this->stack [--this->top ]; g
 Note, T top (void) const f
{ Nesting is purely a visibility issue, it does not return this->stack [this->top , 1]; g
convey additional privileges on Outer or Inner int is empty (void) const f return this->top == 0; g
class relationships int is full (void) const f
return this->top >= this->stack .size ();
 i.e., nesting and access control are separate g
concepts private:
Vector<T> stack ;
{ Also, inner classes do not allocate any addi- int top ;
tional space inside the outer class g;
19 20
Class Data Members (cont'd)
 Important Question: \How do we initial- Base/Member Initialization
ize class data members that are objects Section
of user-de ned types whose constructors
require arguments?"  Four mandatory cases for classes:
1. Initializing base classes (whose constructors re-
 Answer: use the base/member initializa- quire arguments)
tion section
{ That's the part of the constructor after the ':', 2. Initializing user-de ned class data members (whose
following the constructor's parameter list (up constructors require arguments)
to the rst 'f')
3. Initializing reference variables
 Note, it is a good habit to always use the 4. Initializing consts
base/member initialization section
{ e.g., there are less eciency surprises this way
when changes are made  One optional case:
1. Initializing built-in data members
 Base/member initialization section only ap-
plies to constructors
21 22

Base/Member Initialization
Section (cont'd) Class Methods
 e.g.,  Four types of methods
1. Manager functions (constructors, destructors,
class Vector f public: Vector (size t len); /* */ g;
::: and operator=)
class String f public: String (char *str); /* */ g;
{ Allow user-de ned control over class creation,
:::

class Stack : private Vector // Base class


f initialization, assignment, deallocation, and
public: termination
Stack (size t len, char *name)
: Vector (len), name (name), 2. Helper functions
MAX SIZE (len), top (0) fg
// :::
{ \Hidden" functions that assist in the class
private: implementation
String name ; // user-de ned
const int MAX SIZE ; // const
size t top ; // built-in type 3. Accessor functions
//
{ Provide an interface to various components
:::

g;
class Vector Iterator f in the class's state
public:
Vector Iterator (const Vector &v): vr (v), i (0) fg 4. Implementor functions
// :::

private: { Perform the main class operations


Vector &vr ; // reference
size t i ;
g;
23 24
Class Methods (cont'd) The this Pointer
 e.g.,  this is a C++ reserved keyword
// typedef int T;
{ It valid only in non-static method de nitions
template <class T>
class Vector
f  this textually identi es the pointer to the
public: object for which the method is called
// manager
Vector (size t len = 100); class String f
public:
// manager void print (void);
~Vector (void); // :::

private:
// accessor char *str ;
size t size (void) const; // :::

g;
// implementor void String::print (void) f
T &operator[] (size t i); puts (this->str ); // same as puts (str );
g
int main (void) f
private: String s, t;
// helper s.print (); // this == &s
bool in range (size t i) const; t.print (); // this == &t
g; g
25 26

The this Pointer (cont'd) Friends


 The this pointer is most often used ex-  A class may grant access to its private
plicitly to data and methods by including a list of
{ Pass the object (or a pointer or reference to friends in the class de nition, e.g.,
it) to another function
class Vector f
{ Return the object (or a pointer or reference to friend Vector &product (const Vector &, const &Matrix);
it) to another function, e.g., private:
int size ;
#include <ctype.h> //
class String f g;
:::

public: class Matrix f


String &upper case (void);
void print (void) const; friend Vector &product (const Vector &, const &Matrix);
private: private:
char *str ; int size ;
g;
String &String::upper case (void) f //
for (char *cp = this->str ; *cp != 0; cp++)
:::

if (islower (*cp)) g;
*cp = toupper (*cp);
return *this;  Function product can now access private
g parts of both the Vector and Matrix, al-
int main (void) f
String s ("hello"); // this == &s
s.upper case ().print ();
lowing faster access, e.g.,
/* Could also be:
s.upper case (); Vector &product (const Vector &v, const Matrix &m) f
s.print (); int vector size = v.size ;
compare with: int matrix size = m.size ;
cout << s.upper case ();
*/ //
g g
:::

27 28
Friends (cont'd)
 Note, a class may confer friendship on the
following: Friends (cont'd)
1. Entire classes
2. Selected methods in a particular class  Using friends weakens information hiding
3. Ordinary stand-alone functions { In particular, it leads to tightly-coupled imple-
mentations that are overly reliant on certain
naming and implementation details
 Friends allow for controlled violation of
information-hiding
{ e.g., ostream and istream functions:  For this reason, friends are known as the
\goto of access protection mechanisms!"
#include <iostream.h>
class String f
friend ostream &operator << (ostream &, String &);
private:  Note, C++ inline functions reduce the
char *str ;
// :::
need for friends
g; :::

ostream &operator << (ostream &os, String &s) f


os << s.str ;
return os;
g
29 30

Class Vector Example Initialization and Termination


 // File Vector.h (correct wrt initialization  Automatic initialization and termination
and assignment)
activities are supported in C++ via con-
// typedef int T; structors and destructors
template <class T>
class Vector
f  Constructors
public:
~Vector (void); { Allocate data objects upon creation
Vector (size t len = 100, const T init = 0);
size t size (void) const; { Initialize class data members
T &operator[] (size t i);
/* New functions */
Vector (const Vector<T> &v); // Copy constructor { e.g.,
// Assignment operator
Vector<T> &operator= (const Vector<T> &v); template <class T>
protected: Vector<T>::Vector (size t len, const T init)
T &elem (size t i); : size (len), max (len)
private: f
size t size ; if (this->size <= 0)
throw Vector<T>::RANGE ERROR ();
size t max ;
T *buf ; this->buf = new T[this->size ];
bool in range (size t i);
g;
while (--this->size >= 0)
this->buf [this->size ] = init;
 This class solves previous problems with
aliasing and deletion if (verbose logging)
::: log ("constructing Vector object");
g
31 32
Initialization and Termination
Initialization and Termination (cont'd)
(cont'd)
 Without exceptions, handling constructor
 Destructors or destructor failures is very dicult and/or
ugly, e.g.,
{ Deallocate data allocated by the constructor 1. Abort entire program
{ Perform other tasks associated with object ter- 2. Set global (or class instance) ag
mination
{ e.g., 3. Return reference parameter (works for construc-
tors, but not destructors)
template <class T>
Vector<T>::~Vector (void) f 4. Log message and continue :::

delete [] this->buf ;
if (verbose logging)
log ("destructing Vector object");  However, exceptions have their own traps
g and pitfalls :::

33 34

Assignment and Initialization


(cont'd)
Assignment and Initialization  e.g.,
class String f
public:
 Some ADTs must control all copy opera- String (char *t)
tions invoked upon objects : len (t == 0 ? 0 : ::strlen (t)) f
if (this->len == 0)
throw RANGE ERROR ();
this->str = ::strcpy (new char [len + 1], t);
 This is necessary to avoid dynamic mem- g
~String (void) f delete [] this->str ; g
ory aliasing problems caused by \shallow" //
private:
:::

copying size t len , char *str ;


g;
 A String class is a good example of the void foo (void) f
String s1 ("hello");
need for controlling all copy operations ::: String s2 ("world");
s1 = s2; // leads to aliasing
s1[2] = 'x';
assert (s2[2] == 'x'); // will be true!
// :::
// double deletion in destructor calls!
g
35 36
Assignment and Initialization
Assignment and Initialization (cont'd)
(cont'd)  In C++, copy operations include assign-
ment, initialization, parameter passing and
function return, e.g.,
s1 s2
#include "Vector.h"
extern Vector<int> bar (Vector<int>);
void foo (void) f
Vector<int> v1 (100);
world Vector<int> v2 = v1; // Initialize new v2 from v1
// same as Vector v2 (v1);
v1 = v2; // Vector assign v2 to v1
v2 = bar (v1); // Pass and return Vectors
g
 Note that both s1.s and s2.s point to the  Note, parameter passing and function re-
dynamically allocated bu er storing "world" turn of objects by value is treated using
(this is known as \aliasing") initialization semantics via the \copy con-
structor"
37 38

Assignment and Initialization Assignment and Initialization


(cont'd) (cont'd)
 Assignment is di erent than initialization,  and one for assignment (the assignment
operator), e.g.,
:::
since the left hand object already exists
for assignment
template <class T>
Vector<T> &Vector<T>::operator= (const Vector<T> &v)
 Therefore, C++ provides two related, but f
di erent operators, one for initialization if (this != &v) f
(the copy constructor, which also handles if (this->max < v.size ) f
parameter passing and return of objects delete [] this->buf ;
from functions) ::: this->buf = new T[v.size ];
this->max = v.size ;
template <class T> g
Vector<T>::Vector (const Vector &v) this->size = v.size ;
: size (v.size ), max (v.max), buf (new T[v.max])
f for (size t i = 0; i < this->size ; i++)
for (size t i = 0; i < this->size ; i++) this->buf [i] = v.buf [i];
this->buf [i] = v.buf [i]; g
if (verbose logging) return *this; // Allows v1 = v2 = v3;
log ("initializing Vector object"); g
g
39 40
Assignment and Initialization
(cont'd)
Assignment and Initialization
 Both constructors and operator = must (cont'd)
be class members and neither are inherited
{ Rationale
 Bottom-line: de ne constructors and operator=
 If a class had a constructor and an operator for almost every non-trivial class :::

=, but a class derived from it did not what


would happen to the derived class members { Also, de ne destructors and copy constructors
which are not part of the base class?! for most classes as well :::

{ Therefore
 If a constructor or operator = is not de ned  Note, you can also de ne compound as-
for the derived class, the compiler-generated
one will use the base class constructors and signment operators, such as operator +=,
operator ='s for each base class (whether which need have nothing to do with op-
user-de ned or compiler-de ned) erator =
 In addition, a memberwise copy (e.g., using
operator =) is used for each of the derived
class members

41 42

Vector Usage Example Restricting Assignment and


 // File main.C
Initialization
#include <stream.h>  Assignment, initialization, and parameter
#include "Vector.h" passing of objects by value may be pro-
hibited by using access control speci ers:
extern atoi (char *);
template <class T>
int main (int argc, char *argv[]) f class Vector f
int size = argc > 1 ? ::atoi (argv[1]) : 10; public:
Vector<int> v1 (size); // defaults to 0 Vector<T> (void); // Default constructor
Vector<int> v2 (v1); //
/* or: :::

Vector<int> v2 = v1; private:


Vector<int> v2 = Vector<int> (v1); Vector<T> &operator= (const Vector<T> &);
Vector<int> v2 = (Vector<int>) v1; */ Vector<T> (const Vector<T> &);
// :::

::srandom (::time (0L)); g


void foo (Vector<int>); // pass-by-value prototype
for (size t i = 0; i < v1.size (); i++) Vector<int> v1;
v1[i] = v2[i] = ::random (); Vector<int> v2 = v1; // Error
Vector<int> v3 (v1.size (), ,1); v2 = v1; // Error
/* Perform a Vector assignment */ foo (v1); // Error
v3 = v1;
for (size t i = 0; i < v3.size (); i++)  Note, these idioms are surprisingly useful
cout << v3[i]; :::

g
43 44
Restricting Assignment and Restricting Assignment and
Initialization (cont'd) Initialization (cont'd)
 Note, a similar trick can be used to pre-  If you declare a class constructor protected
vent static or auto declaration of an ob- then only objects derived from the class
ject, i.e., only allows dynamic objects! can be created
class Foo f { Note, you can also use pure virtual functions
public: to achieve a similar e ect, though it forces the
// use of virtual tables
void dispose (void);
::: :::

private:
//
~Foo (void); // Destructor is private
:::

g;
:::
 e.g.,
Foo f; // error
 Now the only way to declare a Foo object class Foo f protected: Foo (void); g;
is o the heap, using operator new class Bar : private Foo f public Bar (void); g;
Foo f; // Illegal
Bar b; // OK
Foo *f = new Foo;
 Note, the delete operator is no longer accessible
 Note, if Foo's constructor is declared in
delete f; // error! the private section then we can not de-
 Therefore, a dispose function must be provided clare objects of class Bar either (unless
to delete this
class Bar is declared as a friend of Foo)
f->dispose ();
45 46

Overloading Overloading (cont'd)


 C++ allows overloading of all function  Ambiguous cases are rejected by the com-
names and nearly all operators that handle piler, e.g.,
user-de ned types, including:
{ the assignment operator = int foo (int);
int foo (int, int = 10);
foo (100); // ERROR, ambiguous call!
{ the function call operator () foo (100, 101); // OK!
{ the array subscript operator []  A function's return type is not considered
when distinguishing between overloaded in-
{ the pointer operator ->() stances
{ the \comma" operator , { e.g., the following declarations are ambiguous
to the C++ compiler:
{ the auto-increment operator ++ extern int divide (double, double);
extern double divide (double, double);
 You may not overload:  Overloading becomes a hindrance to the
readability of a program when it serves to
{ the scope resolution operator :: remove information
{ the ternary operator ? : { This is especially true of overloading operators!
 e.g., overloading operators += and -= to mean
{ the \dot" operator . push and pop from a Stack ADT
47 48
Overloading (cont'd)
Overloading (cont'd)
 Function name overloading and operator
overloading relieves the programmer from  For another example of why to avoid op-
the lexical complexity of specifying unique erator overloading, consider the following
function identi er names. e.g., expression:
class String f Matrix a, b, c, d;
// various constructors, destructors, //
// and methods omitted :::

a = b + c * d; // *, +, and = are overloaded


friend String operator+ (String&, const char *); // remember, \standard" precedence rules apply
friend String operator+ (String&,String&); :::

friend String operator+ (const char *, String&);


friend ostream &operator<< (ostream &, String &);  This code will be compiled into something
g; like the following:
String str vec[101];
String curly ("curly"); Matrix t1 = c.operator* (d);
String comma (", "); Matrix t2 = b.operator+ (t1);
str vec[13] = "larry"; a.operator= (t2);
String foo = str vec[13] + ", " + curly; destroy t1;
String bar = foo + comma + "and moe"; destroy t2;
/* bar.String::String (
operator+ (operator+ (foo, comma), "and moe")); */
 This may involve many constructor/destructor
void baz (void) f calls and extra memory copying
cout << bar << "\n"; :::

// prints "larry, curly, and moe"


g
49 50

Overloading (cont'd)
Overloading (cont'd)
 Bottom-line: do not use operator over-
 There are two issues to consider when loading unless absolutely necessary!
composing overloaded operators in expres-
sions, e.g.,
{ Two issues to
 Instead, many operations may be written
using functions with explicit arguments,
e.g.,
1. Memory Management
Matrix a, b, c, d;
 Creation and destruction of temporary vari-
ables :::
Matrix t (b);
t.add (c);
 Where is memory for return values allo- t.mult (d);
cated? a = t;

2. Error Handling  or de ne and use the short-hand operator


x= instead:
 e.g., what happens if a constructor for a
temporary object fails in an expression? Matrix a (c);
a *= d;
 This requires some type of exception han- a += b;
dling
 Note that this is the same as
a = b + c * d;
51 52
Parameterized Types Parameterized Types
 Here's the Vector class again (this time
 Parameterized types serve to describe gen- using a default parameter for the type)
eral container class data structures that template <class T = int>
have identical implementations, regardless class Vector
of the elements they are composed of f
public:
Vector (size t len): size (len),
 The C++ parameterized type scheme al- buf (new T[size < 0 ? 1 : size ]) fg
lows \lazy instantiation" T &operator[] (size t i) f return this->buf [i]; g
// :::

{ i.e., the compiler need not generate de nitions private;


for template methods that are not used size t size ; /* Note, this must come rst!!! */
T *buf ;
g;
Vector<> v1 (20); // int by default:::

 ANSI/ISO C++ also supports template Vector<String> v2 (30);


speci ers, that allow a programmer to \pre- typedef Vector<Complex> COMPLEX VECTOR;
instantiate" certain parameterized types, COMPLEX VECTOR v3 (40);
e.g., v1[1] = 20;
v2[3] = "hello";
template class Vector<int>; v3[10] = Complex (1.0, 1.1);
v1[2] = "hello"; // ERROR!
53 54

Parameterized Types (cont'd)


 e.g., Parameterized Types (cont'd)
Vector<int> *foo (size t size) f
// An array of size number of doubles  Note that we could also use templates to
Vector<double> vd (size); // constructor called supply the size of a vector at compile-time
(more ecient, but less exible)
// A dynamically allocated array of size chars
Vector<char> *vc = new Vector<char>(size); template <class T = int, size t SIZE = 100>
class Vector
// size arrays of 100 ints f
Vector<int> *vi = new Vector<int>[size]; public:
Vector (void): size (SIZE) fg
/* ::: */ T &operator[] (size t i) f return this->buf [i]; g
delete vc; /* Destructor for vc called */ private:
size t size ;
// won't be deallocated until delete is called! T buf[SIZE];
return vi; g;
/* Destructor called for auto variable vd */
g  This would be used as follows:
 Usage Vector<double, 1000> v;
Vector<int> *va = foo (10);
assert (va[1].size () == 100);
delete [] va; /* Call 10 destructors */
55 56
Parameterized Types (cont'd) Exception Handling Overview
 C++ templates may also be used to pa-  Exception handling provides a disciplined
rameterize functions, e.g., way of dealing with erroneous run-time
template <class T> inline void events
swap (T &x, T &y) f
T t = x;
x = y;  When used properly, exception handling
y = t; makes functions easier to understand be-
g
cause they separate out error code from
int main (void) f normal control ow
int a = 10, b = 20;
double d = 10.0, e = 20.0;
char c = 'a', s = 'b';  C++ exceptions may throw and catch
arbitrary C++ objects
swap (a, b); { Therefore, an unlimited amount of information
swap (d, e); may be passed along with the exception indi-
swap (c, s); cation
g

 Note that the C++ compiler is responsi-  The termination (rather than resumption)
ble for generating all the necessary code :::
model of exception handling is used
57 58

Limitations of Exception Handling


Exception Handling Examples
 Exception handling may be costly in terms
of time/space eciency and portability  Without exceptions:
{ e.g., it may be inecient even if exceptions
are not used or not raised during a program's Stack s;
execution int i;
// :::

if (!s.is full ()) s.push (10);


else /* */
 Exception handling is not appropriate for
:::

//
all forms of error-handling, e.g.,
:::

if (!s.is empty ()) i = s.pop ();


else /* */
{ If immediate handling or precise context is re-
:::

quired
 Versus
{ If \error" case may occur frequently
Stack s;
 e.g., reaching end of linked list int i;
try f s.push (10);
// :::

i = s.pop ();
 Exception handling can be hard to pro- g
catch (Stack::UNDERFLOW &e) f /* */ g :::

gram correctly catch (Stack::OVERFLOW &e) f /* */ g :::

59 60
Another C++ Exception Handling
Example Iterators
 Note the sublte chances for errors :::
 Iterators allow applications to loop through
class xxii f elements of some ADT without depend-
public: ing upon knowledge of its implementation
xxii (const String &r): reason (r) fg
String reason ; details
g;
int g (const String &s) f
String null ("");
if (s == null) throw xxii ("null string");  There are a number of di erent techniques
// destructors are automatically called! for implementing iterators
//
g
:::

int f (const String &s) f { Each has advantages and disadvantages


try f
String s1 (s);
char *s2 = new char[100]; // careful
//
:::

g (s1);  Other design issues:


:::

delete [] s2;
return 1; { Providing a copy of each data item vs. provid-
g
catch (xxii &e) f ing a reference to each data item?
cerr << "g() failed, " << e.reason ;
return 22;
g { How to handle concurrency and insertion/deletion
catch ( ) f while iterator(s) are running
cerr << "unknown error occurred!";
:::

return ,1;
g
g
61 62

Iterators (cont'd)
 Three primary methods of designing iter-
ators Pointer to Function Iterator
1. Pass a pointer to a function
{ Not very OO :::
 e.g.,
{ Clumsy way to handle shared data #include <stream.h>
template <class T>
:::

2. Use in-class iterators (a.k.a. passive or internal class Vector f


iterators) public:
/* Same as before */
{ Requires modi cation of class interface int apply (void (*ptf) (T &)) f
for (int i = 0; i < this->size (); i++)
{ Generally not reentrant ::: (*ptf) (this->buf[i]);
g
3. Use out-of-class iterators (a.k.a. active or ex- g
ternal iterator) template <class T> void f (T &i) f
cout << i << endl;
{ Handles multiple simultaneously active iter- g
ators Vector<int> v (100);
// :::

{ May require special access to original class v.apply (f);


internals :::

 i.e., use \friends"


63 64
In-class Iterator Out-of-class Iterator
 e.g.,  e.g.,
#include <stream.h> #include <stream.h>
#include "Vector.h"
template <class T> template <class T>
class Vector f class Vector Iterator f
public: public:
// Same as before Vector Iterator (const Vector<T> &v)
: i (0), vr (v) fg
void reset (void) f this->i = 0; g bool advance (void) f
bool advance (void) f return this->i ++ < this->vr .size ();
return this->i ++ < this->size (); g
g T value (void) f
return this->vr [this->i , 1];
T value (void) f g
return this->buf[this->i , 1]; private:
g Vector<T> &vr ;
private: size t i ;
g;
/* Same as before */ Vector<int> v (100);
size t i ; Vector Iterator<int> iter (v);
g; while (iter.advance () != false)
Vector<int> v (100); cout << "value = " << iter.value () << "\n";
// :::
 Note, this particular scheme does not re-
for (v.reset (); v.advance () != false; ) quire that Vector Iterator be declared as
cout << "value = " << v.value () << "\n"; a friend of class Vector
{ However, for eciency reasons this is often
 Note, this approach is not re-entrant ::: necessary in more complex ADTs
65 66

References
Miscellaneous ADT Issues in
 Parameters, return values, and variables
C++ can all be de ned as \references"
{ This is primarily done for eciency
 References
 Call-by-reference can be used to avoid the
run-time impact of passing large arguments
 const methods by value
{ Note, there is a trade-o between indirection
vs copying
 static methods
struct Huge f int size ; int array [100000]; g;
int total (const Huge &h) f
int count = 0;
 static data members for (int i = 0; i < h.size ; i++)
count += h.array [i];
return count;
g
 mutable Type Quali er
Huge h;

 Arrays of class objects int main (void) f


/* */
:::
// Small parameter passing cost
int count = total (h);
:::

g
67 68
References (cont'd)
 The following behaves like Pascal's VAR
parameter passing mechanism (a.k.a. call-
by-reference): References (cont'd)
double square (double &x) f return x *= x; g
int bar (void) f  A function can also return a reference to
double foo = 10.0; an object, i.e., an lvalue
square (foo);
cout << foo; // prints 100.0 { Avoids cost of returning by an object by value
g
 In C this would be written using explicit { Allows the function call to be an lvalue
dereferencing: Employee &boss of (Employee &);
Employee smith, jones, vacant;
double square (double *x) f return *x *= *x; g if (boss of (smith) == jones)
int bar (void) f boss of (smith) = vacant;
double foo = 10.0;
square (&foo); { Note, this is often done with operator[], e.g.,
printf ("%f", foo); /* prints 100.0 */
g Vector<int> v (10);
v[3] = 100; // v.operator[] (3) = 100;
 Note, reference variables may lead to sub- int i = v[3]; // int i = v.operator[] (3);
tle aliasing problems when combined with
side-e ects:
cout << (square (foo) * foo);
// output result is not de ned!
69 70

References (cont'd) Const Methods


 References are implemented similarly to  When a user-de ned class object is de-
const pointers. Conceptually, the di er- clared as const, its methods cannot be
ences between references and pointers are: called unless they are declared to be const
{ Pointers are rst class objects, references are
methods
not { i.e., a const method must not modify its mem-
 e.g., you can have an array of pointers, but ber data directly
you can't have an array of references
{ References must refer to an actual object, but  This allows read-only user-de ned objects
pointers can refer to lots of other things that to function correctly, e.g.,
aren't objects, e.g.,
 Pointers can refer to the special value 0 in class Point f
C++ (often referred to as NULL) public:
Point (int x, int y): x (x), y (y) fg
 Also, pointers can legitimately refer to a lo- int dist (void) const f
cation one past the end of an array return ::sqrt (this->x * this->x
+ this->y * this->y );
g
void move (int dx, int dy) f
 In general, use of references is safer, less this->x += dx; this->y += dy;
g
ambiguous, and much more restricted than private:
int x , y ;
pointers (this is both good and bad, of g;
course) const Point p (10, 20);
int d = p.dist (); // OK
p.move (3, 5); // ERROR
71 72
Static Data Members Static Methods
 A static data member has exactly one in-  A static method may be called on an ob-
stantiation for the entire class (as opposed
to one for each object in the class), e.g., ject of a class, or on the class itself with-
class Foo f out supplying an object (unlike non-static
public: methods ) :::
int a ;
private:
// Must be de ned exactly once outside header!  Note, there is no this pointer in a static
// (usually in corresponding .C le) method
static int s ;
g; { i.e., a static method cannot access non-static
Foo x, y, z; class data and functions
 Note: class Foo f
public:
{ There are three distinct addresses for Foo::a static int get s1 (void) f
(i.e., &x.a , &y.a , &z.a ) this->a = 10; /* ERROR! */
return Foo::s ;
g
{ There is only one Foo::s, however ::: int get s2 (void) f
this->a = 10; /* OK */
return Foo::s ;
g
 Also note: private:
int a ;
&Foo::s == ( int *); static int s ;
&Foo::a == ( int Foo::*); // pointer to data member g;
73 74

Mutable Type Quali er


 The constness of an object's storage is
Static Methods (cont'd) determined by whether the object is con-
structed as const
 The following calls are legal:
Foo f;
 An attempt to modify the contents of const
int i1, i2, i3, i4;
storage (via casting of pointers or other
i1 = Foo::get s1 ();
tricks) results in unde ned behavior
i2 = f.get s2 (); { It is possible (though not encouraged) to \cast-
i3 = f.get s1 (); away" the constness of an object. This is not
i4 = Foo::get s2 (); // error guaranteed to be portable or correct, however!
 Note: const int i = 10;
// :::

* (int *) &i = 100; // Asking for trouble!


&Foo::get s1 == int (*)(void);
// pointer to method  If a data member is declared with the stor-
&Foo::get s2 == int (Foo::*)(void);
age class mutable, then that member is
modi able even if the containing object is
const
75 76
Mutable Type Quali er (cont'd)
 e.g.,
Mutable Type Quali er (cont'd)
class Foo f
public:  A consequence of mutable is that a ob-
Foo (int a, int b): i (a), j (b) fg ject is ROMable if
mutable int i ;
int j ; 1. Its class doesn't have any mutable data mem-
bers
g;
const Foo bar; 2. The compiler can gure out its contents after
construction at compile time
// the following must be written in a context with
// access rights to Foo::i and Foo::j . 3. The compiler can cope with any side e ects of
the constructor and destructor
bar.i = 5; // well formed and de ned
bar.j = 5; // not well-formed { or can determine that there aren't any
*(int *)(&bar.j ) = 5; // well-formed but unde ned behavior
// better style, but still unde ned behavior
if (int *i = const cast<int *>(&bar.j ))
i = 5;

77 78

Arrays of objects
 In order to create an array of objects that Anonymous Unions
have constructors, one constructor must
take no arguments  A union is a structure who member ob-
{ Either directly or via default arguments for all jects all begin at o set zero and whose
formal parameters size is sucient to contain any of its mem-
ber objects
{ e.g.,
{ They are often used to save space
Vector<Vector<int>> vector vector1;
Vector<int> vector vector2[100];
Vector<int> *vector vector ptr = new Vector<int>[size];
 A union of the form union f member-list
 The constructor is called for each element g; is called an anonymous union; it de nes
an unnamed object
{ Uses a library routine called vec new: : :
{ The union elds are used directly without the
{ Often not re-entrant :::
usual member access syntax, e.g.,
void f (void) f
union f int a ; char *p ; g;
 If array created dynamically via new, then a = 1; p = "Hello World\n";
delete must use an empty [] // a and p have the same address!
{ This instructs the compiler to call the destruc- // i.e., &a == &p
tor the correct number of times, e.g., g
delete [] vector vector ptr;
79 80
Anonymous Unions (cont'd)
Anonymous Unions (cont'd)  Some restrictions apply:
 Here's an example that illustrates a typical { Unions in general
way of using unions, e.g.,
 A union may not be used as a base class and
struct Types f can have no virtual functions
enum Type fINT, DOUBLE, CHARg type ;
union f int i ; double d ; char c ; g;  An object of a class with a constructor or
g t; destructor or a user-de ned assignment op-
if (t.type == Types::DOUBLE) t.d = 100.02; erator cannot be a member of a union

// Q: \what is the total size of STRUCT Types?"  A union can have no static data members
// Q: \What if UNION were changed to STRUCT?"
{ Anonymous unions
 Note that C++ provides other language  Global anonymous unions must be declared
features that makes unions less necessary static
(compared to C)
 An anonymous union may not have private
{ e.g., inheritance with dynamic binding or protected members
 An anonymous union may not have methods

81 82

Summary
 A major contribution of C++ is its sup-
port for de ning abstract data types (ADTs),
e.g.,
{ Classes
{ Parameterized types
{ Exception handling

 For many systems, successfully utilizing


C++'s ADT support is more important
than using the OO features of the lan-
guage, e.g.,
{ Inheritance
{ Dynamic binding

83

S-ar putea să vă placă și