beginslide oop-1 simple containers in c++ ulist: simple container class uiterators umemory...
TRANSCRIPT
bEgInSlIdE
OOP-1
Simple Containers in C++ Simple Containers in C++
List: simple container class Iterators Memory Allocation Responsibility Mixed type collections Abstract Container Classes Non-Intrusive lists
bEgInSlIdE
OOP-2
IteratorsIterators
ii
const N = 100;double a[N];
for (int i = 0; i < N; i++) {... // Process a[i]...
}
const N = 100;double a[N];
for (int i = 0; i < N; i++) {... // Process a[i]...
}
An iterator is used for iterating (traversing/scanning) over all members of a container. Required operations are:
Declare Initialize Get element Increment (Decrement?) Check if last
Array iterators are just integers...
0 1 i i-1 N-1
bEgInSlIdE
OOP-3
List Container ClassList Container Class
void driver(void){
List mid_east;
mid_east.insert(new Leader("Assad", "Syria"));mid_east.insert(new Leader("Peres", "Israel"));mid_east.insert(new Leader("Hussein", "Jordan"));
List::Iterator i(mid_east);for (; i; ((Leader *)i.next())->print())
;
mid_east.insert(new Leader("Mubarak", "Egypt"));for (i.reset(); i; ((Leader *)i.next())->print())
;}
void driver(void){
List mid_east;
mid_east.insert(new Leader("Assad", "Syria"));mid_east.insert(new Leader("Peres", "Israel"));mid_east.insert(new Leader("Hussein", "Jordan"));
List::Iterator i(mid_east);for (; i; ((Leader *)i.next())->print())
;
mid_east.insert(new Leader("Mubarak", "Egypt"));for (i.reset(); i; ((Leader *)i.next())->print())
;}
Generalization: array with integer iterators - into a generalized list container with typical list operations, which could be used to store objects of any type.
We would like our container class used as follows:
bEgInSlIdE
OOP-4
Memory Allocation ResponsibilitiesMemory Allocation Responsibilities What’s stored in the container?What’s stored in the container?
objectsContainer must make a copy of all stored objects and be
responsible for allocating and deallocating them. pointers
Who allocates memory for the stored objects? • User - Automatic objects cannot be stored.
• Container - Stored objects are created using copy constructor.
Who deallocates the objects?• User - The user code must keep record of all objects stored
in the container, so that it could erase them.
• Container - Usually the preferred way.
Node::Node(const String& s) : str(s){}Node::Node(const String& s) : str(s){}
Node::Node(const String* s) : sptr(s){}Node::Node(const String* s) : sptr(s){}
Node::Node(const String& s) : sptr(new String(s)){}Node::Node(const String& s) : sptr(new String(s)){}
bEgInSlIdE
OOP-5
Memory Allocation and Deallocation Memory Allocation and Deallocation Responsibilities with Our InterfaceResponsibilities with Our Interface
void driver(void){
List mid_east;
mid_east.insert(new Leader("Assad", "Syria"));mid_east.insert(new Leader("Peres", "Israel"));mid_east.insert(new Leader("Hussein", "Jordan"));
List::Iterator i(mid_east);for (; i; ((Leader *)i.next())->print())
;
mid_east.insert(new Leader("Mubarak", "Egypt"));for (i.reset(); i; ((Leader *)i.next())->print())
;}
void driver(void){
List mid_east;
mid_east.insert(new Leader("Assad", "Syria"));mid_east.insert(new Leader("Peres", "Israel"));mid_east.insert(new Leader("Hussein", "Jordan"));
List::Iterator i(mid_east);for (; i; ((Leader *)i.next())->print())
;
mid_east.insert(new Leader("Mubarak", "Egypt"));for (i.reset(); i; ((Leader *)i.next())->print())
;}
Allocation in user codeThe container stores pointers to objects allocated in user code.
Allocation in user codeThe container stores pointers to objects allocated in user code.
Deallocation in container codeThe List destructor mid_east.~List executed when driver() returns must delete all the inserted items
Deallocation in container codeThe List destructor mid_east.~List executed when driver() returns must delete all the inserted items
bEgInSlIdE
OOP-6
The List InterfaceThe List Interface
class List { public: class Node {
...};
class Iterator {
...};
void insert(Node *object) {object->next = first.next;first.next = object;
} .... private:
Node first; // Dummy record};
class List { public: class Node {
...};
class Iterator {
...};
void insert(Node *object) {object->next = first.next;first.next = object;
} .... private:
Node first; // Dummy record};
Nested type definition are used mainly for scope definition and for minimizing the pollution of the global name space.
Nested type definition are used mainly for scope definition and for minimizing the pollution of the global name space.
Indeed, the Ansi-C++ committee decided to add a namespace keyword to C++.
Indeed, the Ansi-C++ committee decided to add a namespace keyword to C++.
bEgInSlIdE
OOP-7
Declare and initialize - List::iterator i(mid_east)
Initialize - i.reset() Get element - Value of i.next() Increment - i.next() Check if last - Cast to int. If 0 then list exhausted.
List IteratorsList Iterators
...
List::Iterator i(mid_east);for (; i; ((Leader *)i.next())->print())
;...
for (i.reset(); i; ((Leader *)i.next())->print());
...
List::Iterator i(mid_east);for (; i; ((Leader *)i.next())->print())
;...
for (i.reset(); i; ((Leader *)i.next())->print());
Is next a mutator or an observer?
bEgInSlIdE
OOP-8
Inheritance and Heterogeneous Inheritance and Heterogeneous ContainersContainers
class List {...class Node {
Node *next;Node(Node *n): next(n) {}friend class List;
protected:Node(void): next(NULL) {}virtual ~Node(void) { delete next; }
};...
};
class List {...class Node {
Node *next;Node(Node *n): next(n) {}friend class List;
protected:Node(void): next(NULL) {}virtual ~Node(void) { delete next; }
};...
};Recursive deletion is not terribly efficient...
Recursive deletion is not terribly efficient...
Note that delete NULL is perfectly legal in C++!
Note that delete NULL is perfectly legal in C++!
The List::Node data type is nothing but its identity and linkage information.
The List data type may contain any type derived from List::Node.
bEgInSlIdE
OOP-9
Inheriting From List::NodeInheriting From List::Node
#include <iostream.h>
class Leader: public List::Node {String name;
String country;public:
Leader(const char *n, const char *c): name(n), country(c) {}void print(void) {
cout << name << ';' << country << '\n';}
};
#include <iostream.h>
class Leader: public List::Node {String name;
String country;public:
Leader(const char *n, const char *c): name(n), country(c) {}void print(void) {
cout << name << ';' << country << '\n';}
};
Leader’s constructor will invoke List::Node’s default constructor
Leader’s destructor will invoke List::Node’s destructor
bEgInSlIdE
OOP-10
The Type Hierarchy so FarThe Type Hierarchy so Far
ListList List::NodeList::Node List::IteratorList::Iterator
LeaderLeader
The nested class definitions are done only for minimizing pollution of the global name space
bEgInSlIdE
OOP-11
New & DeleteNew & Delete
class List {...
class Node {...
protected: virtual ~Node(void) { ... }public:
void *operator new(size_t size) {return ::operator new(size);
void operator delete(void *p, size_t size) {::operator delete(p);};...
};
class List {...
class Node {...
protected: virtual ~Node(void) { ... }public:
void *operator new(size_t size) {return ::operator new(size);
void operator delete(void *p, size_t size) {::operator delete(p);};...
};
Employee::new for all classes derived from Employee
Employee::new for all classes derived from Employee
Always static, even if not declared as such!Always static, even if not declared as such!
The size parameter is that of the actual object allocated/deallocated.
The size parameter is that of the actual object allocated/deallocated.
bEgInSlIdE
OOP-12
Protected Data MembersProtected Data Members
Derived classes cannot access private parts (Or else it would have been a breach of privacy)
Protected data and methods can be accessed only by:
derived classes members friends
bEgInSlIdE
OOP-13
Protected ConstructorsProtected Constructors
class List {public:
class Iterator;class Node {
friend class List;friend class Iterator;Node *next;Node(Node *n): next(n) {}
protected:Node(void): next(0) {}virtual ~Node(void) { delete next; }
};...
};
class List {public:
class Iterator;class Node {
friend class List;friend class Iterator;Node *next;Node(Node *n): next(n) {}
protected:Node(void): next(0) {}virtual ~Node(void) { delete next; }
};...
};
The next field and the non-default constructor can be used by List and List::Iterator only.
Leader can only use the default constructor of List::Node.
bEgInSlIdE
OOP-14
List Destruction and Virtual List Destruction and Virtual DestructorsDestructors
ListList
class List {...class Node {
...protected:
virtual ~Node(void) { delete next; }};Node first;
};
class List {...class Node {
...protected:
virtual ~Node(void) { delete next; }};Node first;
};
Destruction sequence List goes out of scope List destructed ט First node destructed ט Other nodes destructed recursively ט
~List::Node must be virtual to guarantee that ~Leader is called when the list is destructed
LeaderLeader
Node
LeaderLeader
Node
LeaderLeader
Node
bEgInSlIdE
OOP-15
Iterator ImplementationIterator Implementation
class List {...class
public:class Iterator {
Node *first, *curr;public:
Iterator(List& l) { first = &l.first; reset(); }void reset(void) { curr = first->next; }operator int(void) { return curr != NULL; }Node *next(void) {
Node* tmp = curr;if (curr != NULL) curr = curr->next;return tmp;
}};...Node first; // Dummy node used as first list element
};
class List {...class
public:class Iterator {
Node *first, *curr;public:
Iterator(List& l) { first = &l.first; reset(); }void reset(void) { curr = first->next; }operator int(void) { return curr != NULL; }Node *next(void) {
Node* tmp = curr;if (curr != NULL) curr = curr->next;return tmp;
}};...Node first; // Dummy node used as first list element
};
bEgInSlIdE
OOP-16
Summary: List InterfaceSummary: List Interfaceclass List {
public:class Node {
...};class Iterator {
...};
void insert(Node *object) { object->next = first.next; first.next = object;
}};
class List {public:class Node {
...};class Iterator {
...};
void insert(Node *object) { object->next = first.next; first.next = object;
}};
List stores any class derived from List::Node. Exported operations are insert and iteration Only. Constructor and destructor are generated
automatically.
bEgInSlIdE
OOP-17
QuizQuizvoid driver(void){
List mid_east;
mid_east.insert(new Leader("Assad","Syria"));mid_east.insert(new Leader("Peres","Israel"));mid_east.insert(new Leader("Hussein","Jordan"));
List::Iterator i(mid_east);for (; i; ((Leader *)i.next())->print())
;
mid_east.insert(new Leader("Mubarak","Egypt"));
for (i.reset(); i; ((Leader *)i.next())->print());
}
void driver(void){
List mid_east;
mid_east.insert(new Leader("Assad","Syria"));mid_east.insert(new Leader("Peres","Israel"));mid_east.insert(new Leader("Hussein","Jordan"));
List::Iterator i(mid_east);for (; i; ((Leader *)i.next())->print())
;
mid_east.insert(new Leader("Mubarak","Egypt"));
for (i.reset(); i; ((Leader *)i.next())->print());
} What will be printed? Describe the allocation and deallocation activities. How is the following an indication of a design problem?
((Leader *)i.next())->print()
bEgInSlIdE
OOP-18
class List {...
class Iterator {Node *first, *curr;
Node *next(void) {Node* tmp = curr;if (curr != NULL) curr = curr->next;return tmp;
}public:
Iterator(List& l) { first = curr = l.first.next; }void reset(void) { curr = first; }operator int(void) { return curr != NULL; }Node * operator()(void){ return curr; }Iterator& operator++(void){ next(); return *this; }
};...
};...for (List::Iterator i(mid_east); i; ++i)
cout << *(Leader *)i();
class List {...
class Iterator {Node *first, *curr;
Node *next(void) {Node* tmp = curr;if (curr != NULL) curr = curr->next;return tmp;
}public:
Iterator(List& l) { first = curr = l.first.next; }void reset(void) { curr = first; }operator int(void) { return curr != NULL; }Node * operator()(void){ return curr; }Iterator& operator++(void){ next(); return *this; }
};...
};...for (List::Iterator i(mid_east); i; ++i)
cout << *(Leader *)i();
Operator Overloading & Iterators Operator Overloading & Iterators
Why prefix operator++?Why prefix operator++?Assumption: operator<< is overloaded for class Leader
Assumption: operator<< is overloaded for class Leader
bEgInSlIdE
OOP-19
Iteration FunctionsIteration Functions
void ForEach( List& list, void (*func)(List::Node *current))
{for (List::Iterator i(l); i; ++i) func(i());
}
List::Node *FirstThat(List& list, int (*func)(List::Node *current))
{List::Node *current;
for (List::Iterator i(l); i; ++i)if (func(current = i()))
return current;
return (List::Node *)0;}
void ForEach( List& list, void (*func)(List::Node *current))
{for (List::Iterator i(l); i; ++i) func(i());
}
List::Node *FirstThat(List& list, int (*func)(List::Node *current))
{List::Node *current;
for (List::Iterator i(l); i; ++i)if (func(current = i()))
return current;
return (List::Node *)0;}
Can be used for implementing general purpose iteration and search
bEgInSlIdE
OOP-20
Using Iteration FunctionsUsing Iteration Functions
void Print(List::Node *n) {
cout << (Leader *)n; }...ForEach(mid_east,Print);...Leader Who, *found;...int Search(List::Node *n) {
return (Leader *)n == Who; }...if ((found = FirstThat(mid_east,Search)) != NULL)
...
void Print(List::Node *n) {
cout << (Leader *)n; }...ForEach(mid_east,Print);...Leader Who, *found;...int Search(List::Node *n) {
return (Leader *)n == Who; }...if ((found = FirstThat(mid_east,Search)) != NULL)
...
bEgInSlIdE
OOP-21
Memory Allocation Responsibilities:Memory Allocation Responsibilities:Summary and Typical ErrorsSummary and Typical Errors
Better to store pointers in a container Better let the container own the elements:
Elements destroyed when container destroyed.
Typical errors are: Store an automatic object Store a global/static object Store the same object in more than one container. Destroying explicitly an element in the container.