1
Chapter 5: Composite Pattern & Iterator Pattern
Aggregation and Composition
2
Hierarchical Data
Lots of application store data in trees or other hierarchical structure.
➢ Graphical models
➢ Drawings
➢ XML Documents
➢ Equations
➢ Assemblies
➢ Syntax representations
➢ Decision trees
3
We’ve been seeing this a lot
And we’ll be seeing it more…
Or at least variations on the theme
I’ve added operations
to Group to add and
remove children.
4
The Problem
What if I want to be able to iterate over the tree in
some other order?
What if I am only interested in part of the tree?
5
I could add an iterator to Group, but…
Any problems with this?
6
We usually don’t have a pointer to Group
We have a
pointer to this
We would need a
pointer to this
7
The Composite Pattern
A simple idea:
Add operations to manage children to the abstract base class.
8
The Complete UML
9
What should the default (base class) version
do?
10
What should the default (base class) version
do? Add and Remove can just be empty
functions
GetIterator() should return a Null
Iterator (what’s that?)
11
A Null Iterator for Animals
class NullIterator : public CIterator<CAnimal *>
{
public:
virtual void First() {}
virtual void Next() {}
virtual bool IsDone() {return true;}
virtual CAnimal * Current() {return NULL;}
};
12
A General Purpose Null Iterator
template <class T> class CNullIterator : public CIterator<T>
{
public:
virtual void First() {}
virtual void Next() {}
virtual bool IsDone() {return true;}
virtual T Current() {return 0;}
};
Drawable *GetIterator() {return new CNullIterator<Drawable *>();}
13
Caveats…
The text devotes considerable discussion (pages 167-168) to the question
of putting the child management functions (add, remove) in the base
class.
Advantages:
Consistent interface
Disadvantages:
Not meaningful for leaf nodes (nodes with no children)
Violates normal object oriented design guidelines
If not in the base class, you need a virtual function to get a pointer to the
composite class. (Like you got a pointer to a grass tile).
14
Where to apply composite pattern
• The objects have part-of relationships.
• There are common operations. You want clients to be able to ignore the
difference between compositions of objects and individual objects. Clients
will treat all objects in the composite structure uniformly.
• Example problems:
• Graphics: line, rectangle, text, picture
• Part-of relationship: a picture may contain lines, rectangles, texts, and pictures.
• Common operations: draw()
• Equipments: floppydisk, network card, chassis, cabinet
• Part-of relationship : a chassis may contain floppydisks, network cards, etc; and a cabinet
may contain floppydisks, network cards, chassis, and cabinets.
• Common operations: power(), price()
• Expressions: variables, literals, negate, binary expressions
• Note: a binary expression is composed by two expressions (add, substract, multiply,
divide).
• Common operations: value()
• Recursive composition.
15
What was this?
16
UML Associations
Do we remember what an association is?
What does this mean?
17
What are differences between these two
associations?
18
Can we say?
➢ One is a part of the other?
➢ Operations on one apply to its parts?
➢ One is subordinate to the other?
19
Aggregation
➢ One is a part of the other?
➢ Operations on one apply to its parts?
➢ One is subordinate to the other?
Yes
We call this type of an association an aggregation. We add the open
diamond symbol to the association as seen below.
Aggregation
No
20
Composition
A composition is an aggregation with two additional requirements:
➢The part can be associated with at most one assembly
➢The part has a coincident lifetime with the assembly
(is destroyed when the assembly is destroyed)
Composition
Aggregation
Association
21
Example 3D Model
22
New UML Symbols
Nested Class
Aggregation
Composition
Parameterized Class
(Template Class)
23
The Iterator Pattern
template <class T> class CIterator
{
public:
virtual void First() = 0;
virtual void Next() = 0;
virtual bool IsDone() = 0;
virtual T Current() = 0;
protected:
CIterator() {}
CIterator(CIterator &) {}
};
24
This is bad…
class CCity
{
public:
// ...
std::list<CTile *> &GetTiles() {return mTiles;}
private:
std::list<CTile *> mTiles;
};
Why???
list<CTile *> &tiles = mCity->GetTiles();
for(list<CTile *>::iterator t=tiles.begin(); t != tiles.end(); t++)
{
// Do something…
}
25
This is bad…
class CCity
{
public:
// ...
std::list<CTile *> &GetTiles() {return mTiles;}
private:
std::list<CTile *> mTiles;
};
“All right, Ryan, we just unzipped our fly.”
Capt. Bart Mancuso, The Hunt for Red October
list<CTile *> &tiles = mCity->GetTiles();
for(list<CTile *>::iterator t=tiles.begin(); t != tiles.end(); t++)
{
// Do something…
}
What if CCity has pointers to tiles for other purposes?
We can do awful things like:
mCity->GetTiles().erase(t);
mCity->GetTiles().push_back(new CUglyTile());
26
This is bad…
class CCity
{
public:
// ...
std::list<CTile *> &GetTiles() {return mTiles;}
private:
std::list<CTile *> mTiles;
};
But, what if we have clients that need to be
able to go through the tiles and check for
something or another?
list<CTile *> &tiles = mCity->GetTiles();
for(list<CTile *>::iterator t=tiles.begin(); t != tiles.end(); t++)
{
// Do something…
}
27
One Alternative
Visitor pattern
➢ Wonderful if we want to look at everything
➢ Avoids issues of down-casting
➢ But, sometimes it is not the ideal solution
28
One Alternative
Visitor pattern downsides
➢ Fixed traversal order
➢ Visits everything, can’t stop when done.
➢ Often interested in only one level of a tree.
We’re not going to replace visitor pattern.
The new pattern we’ll discuss today often is
combined with the visitor pattern.
29
Suppose…
Your radio frequency identification (RFID) system has located
10 animals in a field. Your have the National Animal
Identification System (NAIS) number for each animal (a 15
digit number). You want to find the objects for these 10
animals among your 500.
Any disadvantage to using a visitor?
30
Visitor Disadvantages
Can’t put in a loop (loop is built in)
Can’t stop early (like when we find our 10 animals)
Can’t examine only one level of a hierarchy (tree data)
What if I have 10,000 animals and
our 10 are the first 10?
31
Good vs. Bad
class CFarm
{
public:
CFarm();
virtual ~CFarm();
std::list<CAnimal *> &GetInventoryEvil() {return mInventory;}
Iterator *GetInventory() {return new Iterator(this);}
private:
// The inventory of animals on the farm
std::list<CAnimal *> mInventory;
};
Good
Evil
32
The Iterator Pattern
template <class T> class CIterator
{
public:
virtual void First() = 0;
virtual void Next() = 0;
virtual bool IsDone() = 0;
virtual T Current() = 0;
protected:
CIterator() {}
CIterator(CIterator &) {}
};
➢This is a “template” class.
➢Implemented only in .h file.
➢No accompanying .cpp file.
➢T is effectively “filled in” for us.
CIterator<CAnimal *> *iter = farm.GetInventory();
33
Parameterized (template) classes in UML
In Bouml
Parameter on the class
(Template variable)
template <class T> class CIterator
{
public:
virtual void First() = 0;
virtual void Next() = 0;
virtual bool IsDone() = 0;
virtual T Current() = 0;
protected:
CIterator() {}
CIterator(CIterator &) {}
};
34
How we will use
CIterator<CAnimal *> *iter = farm.GetInventory();
for(iter->First(); !iter->IsDone(); iter->Next())
{
iter->Current()->DisplayAnimal();
}
delete iter;
template <class T> class CIterator
{
public:
virtual void First() = 0;
virtual void Next() = 0;
virtual bool IsDone() = 0;
virtual T Current() = 0;
protected:
CIterator() {}
CIterator(CIterator &) {}
};
Displaying animals
You probably already noticed:
CIterator is Abstract. We’ll do
the work in a derived class.
35
The Concrete Iterator
class Iterator : public CIterator<CAnimal *>
{
public:
Iterator(CFarm *farm) {mFarm = farm; mIter = farm->mInventory.begin();}
Iterator(Iterator &i) {mFarm = i.mFarm; mIter = i.mIter;}
virtual void First() {mIter = mFarm->mInventory.begin();}
virtual void Next() {mIter++;}
virtual bool IsDone() {return mIter == mFarm->mInventory.end();}
virtual CAnimal * Current() {return *mIter;}
private:
Iterator() {}
CFarm *mFarm;
std::list<CAnimal *>::iterator mIter;
};
There should be many questions!
36
class CFarm
{
public:
CFarm();
virtual ~CFarm();
friend class Iterator;
class Iterator : public CIterator<CAnimal *>
{
public:
Iterator(CFarm *farm) {mFarm = farm; mIter = farm->mInventory.begin();}
Iterator(Iterator &i) {mFarm = i.mFarm; mIter = i.mIter;}
virtual void First() {mIter = mFarm->mInventory.begin();}
virtual void Next() {mIter++;}
virtual bool IsDone() {return mIter == mFarm->mInventory.end();}
virtual CAnimal * Current() {return *mIter;}
private:
Iterator() {}
CFarm *mFarm;
std::list<CAnimal *>::iterator mIter;
};
CIterator<CAnimal *> *GetInventory() {return new Iterator(this);}
private:
std::list<CAnimal *> mInventory;
};
Why no “C”
Iterator is a nested class. It
is a class contained inside
CFarm. Since we always
will refer to it outside
CFarm as CFarm::Iterator, I
don’t use the leading C.
37
Nested Classes
Nested classes are members of the class.
We usually use Nested classes to create components associated with a
class (like an iterator). The general rule is that the component be
logically associated with the class and the component would not make
sense without the class.
Nested classes can be public, protected, or private.
38
Nested classes in UML
class CFarm
{
public:
CFarm();
virtual ~CFarm();
friend class Iterator;
class Iterator : public CIterator<CAnimal *>
{
public:
//…
};
//…
};
Circle with + in it.
Indicates Iterator is nested in CFarm
39
What is “friend”?
friend class Iterator;
class Iterator : public CIterator<CAnimal *>
Declaring something a friend means it can access the private members of the
enclosing class.
This is being revised in the C++ standard. The new standard will be that a nested
class has the same privileges as any other member of the class, so friend will not be
necessary.
Microsoft and GCC have already adopted the proposed standard. Some, like IBM,
have not.
In 11.8 [class.access.nest] paragraph 1, change: “The members of a nested class have no special access to
members of an enclosing class, nor to classes or functions that have granted friendship to an enclosing class;
the usual access rules (clause 11 [class.access]) shall be obeyed.”
To: “A nested class is a member and as such has the same access rights as any other member. “
40
An Example Nested Class
class CActor
{
public:
CActor();
//…
class Bone
{
public:
Bone(CActor *actor);
//…
private:
std::wstring mName;
Matrix mTransform;
};
private:
std::vector<Bone> mBones;
};
Circle with + in it.
Indicates Iterator is nested in CFarm
3
41
Any more questions?
class Iterator : public CIterator<CAnimal *>
{
public:
Iterator(CFarm *farm) {mFarm = farm; mIter = farm->mInventory.begin();}
Iterator(Iterator &i) {mFarm = i.mFarm; mIter = i.mIter;}
virtual void First() {mIter = mFarm->mInventory.begin();}
virtual void Next() {mIter++;}
virtual bool IsDone() {return mIter == mFarm->mInventory.end();}
virtual CAnimal * Current() {return *mIter;}
private:
Iterator() {}
CFarm *mFarm;
std::list<CAnimal *>::iterator mIter;
};
42
Why is this better?
class Iterator : public CIterator<CAnimal *>
{
public:
Iterator(CFarm *farm) {mFarm = farm; mIter = farm->mInventory.begin();}
Iterator(Iterator &i) {mFarm = i.mFarm; mIter = i.mIter;}
virtual void First() {mIter = mFarm->mInventory.begin();}
virtual void Next() {mIter++;}
virtual bool IsDone() {return mIter == mFarm->mInventory.end();}
virtual CAnimal * Current() {return *mIter;}
private:
Iterator() {}
CFarm *mFarm;
std::list<CAnimal *>::iterator mIter;
};
CIterator<CAnimal *> *iter = farm.GetInventory();
for(iter->First(); !iter->IsDone(); iter->Next())
{
iter->Current()->DisplayAnimal();
}
delete iter;
What if I were to change how I
store my animals from a list to a
vector?
43
A different version
class Iterator : public CIterator<CAnimal *>
{
public:
Iterator(CFarm *farm) {mFarm = farm; mIter = farm->mInventory.begin();}
Iterator(Iterator &i) {mFarm = i.mFarm; mIter = i.mIter;}
virtual void First() {mIter = mFarm->mInventory.begin();}
virtual void Next() {mIter++;}
virtual bool IsDone() {return mIter == mFarm->mInventory.end();}
virtual CAnimal * Current() {return *mIter;}
private:
Iterator() {}
CFarm *mFarm;
std::vector<CAnimal *>::iterator mIter;
};
CIterator<CAnimal *> *iter = farm.GetInventory();
for(iter->First(); !iter->IsDone(); iter->Next())
{
iter->Current()->DisplayAnimal();
}
delete iter;
No change in how client uses
the iterator
44
What if we change to store the inventory this way
Private in CFarm:
// Number of animals
int mCnt;
// Array of animals
CAnimal **mAnimals;
class Iterator : public CIterator<CAnimal *>
{
public:
Iterator(CFarm *farm) {mFarm = farm; mIter = farm->mInventory.begin();}
Iterator(Iterator &i) {mFarm = i.mFarm; mIter = i.mIter;}
virtual void First() {mIter = mFarm->mInventory.begin();}
virtual void Next() {mIter++;}
virtual bool IsDone() {return mIter == mFarm->mInventory.end();}
virtual CAnimal * Current() {return *mIter;}
private:
Iterator() {}
CFarm *mFarm;
std::vector<CAnimal *>::iterator mIter;
};
What to
change in
this?
45
What if we changed to store the inventory this way
class Iterator : public CIterator<CAnimal *>
{
public:
Iterator(CFarm *farm) {mFarm = farm; mItem = 0;}
Iterator(Iterator &i) {mFarm = i.mFarm; mItem = i.mItem;}
virtual void First() {mItem = 0;}
virtual void Next() {mItem++;}
virtual bool IsDone() {return mItem == mFarm->mCnt;}
virtual CAnimal * Current() {return mFarm->mAnimals[mItem];}
private:
Iterator() {}
CFarm *mFarm;
int mItem;
};
Private in CFarm:
// Number of animals
int mCnt;
// Array of animals
CAnimal *mAnimals;
46
Why is this better?
➢ A uniform iterator interface for many classes
➢ Does not expose internal data structures
➢ Not dependent on internal data structures
47
Should we use…
CIterator<CAnimal *> *iter = farm.GetInventory();
for(iter->First(); !iter->IsDone(); iter->Next())
{
iter->Current()->DisplayAnimal();
}
delete iter;
CFarm::Iterator *iter = farm.GetInventory();
for(iter->First(); !iter->IsDone(); iter->Next())
{
iter->Current()->DisplayAnimal();
}
delete iter;
or
Either works.
May be easier to read.
We can use a copy constructor .
48
Be sure to delete the iterator
CIterator<CAnimal *> *iter = farm.GetInventory();
for(iter->First(); !iter->IsDone(); iter->Next())
{
iter->Current()->DisplayAnimal();
}
delete iter;
49
Our XML example
class XmlTag : public XmlNode
{
public:
XmlTag(const std::wstring name);
~XmlTag();
//…
private:
XmlNode *mChildren; // Pointer to singly linked list of child nodes
XmlAttribute *mAttributes; // Pointer to singly linked list of attributes
};
class XmlNode
{
//…
XmlNode *Next();
};
50
An Alternative from the GoF book
template <class T> class CIteratorPtr
{
public:
CIteratorPtr(CIterator<T> * i) : mIter(i) {}
~CIteratorPtr() {delete mIter;}
CIterator<T> *operator->() {return mIter;}
CIterator<T> &operator*() {return *mIter;}
private:
CIteratorPtr() {}
CIteratorPtr(const CIteratorPtr *) {}
CIteratorPtr &operator=(CIteratorPtr &) {}
CIterator<T> *mIter;
};
This is called a
pointer class. It holds
a pointer to an
iterator and takes
care of deleting the
iterator when the
pointer object is
destroyed.
CIteratorPtr<CAnimal *> iter = farm.GetInventory();
for(iter->First(); !iter->IsDone(); iter->Next())
{
iter->Current()->DisplayAnimal();
}
51
The ever growing farm example
52
Some Caveats
It should always be clearly stated if an interator is invalidated
by other operations, particularly add and remove.
When in doubt, assume the interator is invalidated.
It usually is!
CIteratorPtr<CAnimal *> iter = farm.GetInventory();
for(iter->First(); !iter->IsDone(); iter->Next())
{
if(iter->Current()->IsUgly())
farm.Remove(iter->Current());
}
How might I do something
like this so it will work?
53
END OF CHAPTER

Chapter 5

  • 1.
    1 Chapter 5: CompositePattern & Iterator Pattern Aggregation and Composition
  • 2.
    2 Hierarchical Data Lots ofapplication store data in trees or other hierarchical structure. ➢ Graphical models ➢ Drawings ➢ XML Documents ➢ Equations ➢ Assemblies ➢ Syntax representations ➢ Decision trees
  • 3.
    3 We’ve been seeingthis a lot And we’ll be seeing it more… Or at least variations on the theme I’ve added operations to Group to add and remove children.
  • 4.
    4 The Problem What ifI want to be able to iterate over the tree in some other order? What if I am only interested in part of the tree?
  • 5.
    5 I could addan iterator to Group, but… Any problems with this?
  • 6.
    6 We usually don’thave a pointer to Group We have a pointer to this We would need a pointer to this
  • 7.
    7 The Composite Pattern Asimple idea: Add operations to manage children to the abstract base class.
  • 8.
  • 9.
    9 What should thedefault (base class) version do?
  • 10.
    10 What should thedefault (base class) version do? Add and Remove can just be empty functions GetIterator() should return a Null Iterator (what’s that?)
  • 11.
    11 A Null Iteratorfor Animals class NullIterator : public CIterator<CAnimal *> { public: virtual void First() {} virtual void Next() {} virtual bool IsDone() {return true;} virtual CAnimal * Current() {return NULL;} };
  • 12.
    12 A General PurposeNull Iterator template <class T> class CNullIterator : public CIterator<T> { public: virtual void First() {} virtual void Next() {} virtual bool IsDone() {return true;} virtual T Current() {return 0;} }; Drawable *GetIterator() {return new CNullIterator<Drawable *>();}
  • 13.
    13 Caveats… The text devotesconsiderable discussion (pages 167-168) to the question of putting the child management functions (add, remove) in the base class. Advantages: Consistent interface Disadvantages: Not meaningful for leaf nodes (nodes with no children) Violates normal object oriented design guidelines If not in the base class, you need a virtual function to get a pointer to the composite class. (Like you got a pointer to a grass tile).
  • 14.
    14 Where to applycomposite pattern • The objects have part-of relationships. • There are common operations. You want clients to be able to ignore the difference between compositions of objects and individual objects. Clients will treat all objects in the composite structure uniformly. • Example problems: • Graphics: line, rectangle, text, picture • Part-of relationship: a picture may contain lines, rectangles, texts, and pictures. • Common operations: draw() • Equipments: floppydisk, network card, chassis, cabinet • Part-of relationship : a chassis may contain floppydisks, network cards, etc; and a cabinet may contain floppydisks, network cards, chassis, and cabinets. • Common operations: power(), price() • Expressions: variables, literals, negate, binary expressions • Note: a binary expression is composed by two expressions (add, substract, multiply, divide). • Common operations: value() • Recursive composition.
  • 15.
  • 16.
    16 UML Associations Do weremember what an association is? What does this mean?
  • 17.
    17 What are differencesbetween these two associations?
  • 18.
    18 Can we say? ➢One is a part of the other? ➢ Operations on one apply to its parts? ➢ One is subordinate to the other?
  • 19.
    19 Aggregation ➢ One isa part of the other? ➢ Operations on one apply to its parts? ➢ One is subordinate to the other? Yes We call this type of an association an aggregation. We add the open diamond symbol to the association as seen below. Aggregation No
  • 20.
    20 Composition A composition isan aggregation with two additional requirements: ➢The part can be associated with at most one assembly ➢The part has a coincident lifetime with the assembly (is destroyed when the assembly is destroyed) Composition Aggregation Association
  • 21.
  • 22.
    22 New UML Symbols NestedClass Aggregation Composition Parameterized Class (Template Class)
  • 23.
    23 The Iterator Pattern template<class T> class CIterator { public: virtual void First() = 0; virtual void Next() = 0; virtual bool IsDone() = 0; virtual T Current() = 0; protected: CIterator() {} CIterator(CIterator &) {} };
  • 24.
    24 This is bad… classCCity { public: // ... std::list<CTile *> &GetTiles() {return mTiles;} private: std::list<CTile *> mTiles; }; Why??? list<CTile *> &tiles = mCity->GetTiles(); for(list<CTile *>::iterator t=tiles.begin(); t != tiles.end(); t++) { // Do something… }
  • 25.
    25 This is bad… classCCity { public: // ... std::list<CTile *> &GetTiles() {return mTiles;} private: std::list<CTile *> mTiles; }; “All right, Ryan, we just unzipped our fly.” Capt. Bart Mancuso, The Hunt for Red October list<CTile *> &tiles = mCity->GetTiles(); for(list<CTile *>::iterator t=tiles.begin(); t != tiles.end(); t++) { // Do something… } What if CCity has pointers to tiles for other purposes? We can do awful things like: mCity->GetTiles().erase(t); mCity->GetTiles().push_back(new CUglyTile());
  • 26.
    26 This is bad… classCCity { public: // ... std::list<CTile *> &GetTiles() {return mTiles;} private: std::list<CTile *> mTiles; }; But, what if we have clients that need to be able to go through the tiles and check for something or another? list<CTile *> &tiles = mCity->GetTiles(); for(list<CTile *>::iterator t=tiles.begin(); t != tiles.end(); t++) { // Do something… }
  • 27.
    27 One Alternative Visitor pattern ➢Wonderful if we want to look at everything ➢ Avoids issues of down-casting ➢ But, sometimes it is not the ideal solution
  • 28.
    28 One Alternative Visitor patterndownsides ➢ Fixed traversal order ➢ Visits everything, can’t stop when done. ➢ Often interested in only one level of a tree. We’re not going to replace visitor pattern. The new pattern we’ll discuss today often is combined with the visitor pattern.
  • 29.
    29 Suppose… Your radio frequencyidentification (RFID) system has located 10 animals in a field. Your have the National Animal Identification System (NAIS) number for each animal (a 15 digit number). You want to find the objects for these 10 animals among your 500. Any disadvantage to using a visitor?
  • 30.
    30 Visitor Disadvantages Can’t putin a loop (loop is built in) Can’t stop early (like when we find our 10 animals) Can’t examine only one level of a hierarchy (tree data) What if I have 10,000 animals and our 10 are the first 10?
  • 31.
    31 Good vs. Bad classCFarm { public: CFarm(); virtual ~CFarm(); std::list<CAnimal *> &GetInventoryEvil() {return mInventory;} Iterator *GetInventory() {return new Iterator(this);} private: // The inventory of animals on the farm std::list<CAnimal *> mInventory; }; Good Evil
  • 32.
    32 The Iterator Pattern template<class T> class CIterator { public: virtual void First() = 0; virtual void Next() = 0; virtual bool IsDone() = 0; virtual T Current() = 0; protected: CIterator() {} CIterator(CIterator &) {} }; ➢This is a “template” class. ➢Implemented only in .h file. ➢No accompanying .cpp file. ➢T is effectively “filled in” for us. CIterator<CAnimal *> *iter = farm.GetInventory();
  • 33.
    33 Parameterized (template) classesin UML In Bouml Parameter on the class (Template variable) template <class T> class CIterator { public: virtual void First() = 0; virtual void Next() = 0; virtual bool IsDone() = 0; virtual T Current() = 0; protected: CIterator() {} CIterator(CIterator &) {} };
  • 34.
    34 How we willuse CIterator<CAnimal *> *iter = farm.GetInventory(); for(iter->First(); !iter->IsDone(); iter->Next()) { iter->Current()->DisplayAnimal(); } delete iter; template <class T> class CIterator { public: virtual void First() = 0; virtual void Next() = 0; virtual bool IsDone() = 0; virtual T Current() = 0; protected: CIterator() {} CIterator(CIterator &) {} }; Displaying animals You probably already noticed: CIterator is Abstract. We’ll do the work in a derived class.
  • 35.
    35 The Concrete Iterator classIterator : public CIterator<CAnimal *> { public: Iterator(CFarm *farm) {mFarm = farm; mIter = farm->mInventory.begin();} Iterator(Iterator &i) {mFarm = i.mFarm; mIter = i.mIter;} virtual void First() {mIter = mFarm->mInventory.begin();} virtual void Next() {mIter++;} virtual bool IsDone() {return mIter == mFarm->mInventory.end();} virtual CAnimal * Current() {return *mIter;} private: Iterator() {} CFarm *mFarm; std::list<CAnimal *>::iterator mIter; }; There should be many questions!
  • 36.
    36 class CFarm { public: CFarm(); virtual ~CFarm(); friendclass Iterator; class Iterator : public CIterator<CAnimal *> { public: Iterator(CFarm *farm) {mFarm = farm; mIter = farm->mInventory.begin();} Iterator(Iterator &i) {mFarm = i.mFarm; mIter = i.mIter;} virtual void First() {mIter = mFarm->mInventory.begin();} virtual void Next() {mIter++;} virtual bool IsDone() {return mIter == mFarm->mInventory.end();} virtual CAnimal * Current() {return *mIter;} private: Iterator() {} CFarm *mFarm; std::list<CAnimal *>::iterator mIter; }; CIterator<CAnimal *> *GetInventory() {return new Iterator(this);} private: std::list<CAnimal *> mInventory; }; Why no “C” Iterator is a nested class. It is a class contained inside CFarm. Since we always will refer to it outside CFarm as CFarm::Iterator, I don’t use the leading C.
  • 37.
    37 Nested Classes Nested classesare members of the class. We usually use Nested classes to create components associated with a class (like an iterator). The general rule is that the component be logically associated with the class and the component would not make sense without the class. Nested classes can be public, protected, or private.
  • 38.
    38 Nested classes inUML class CFarm { public: CFarm(); virtual ~CFarm(); friend class Iterator; class Iterator : public CIterator<CAnimal *> { public: //… }; //… }; Circle with + in it. Indicates Iterator is nested in CFarm
  • 39.
    39 What is “friend”? friendclass Iterator; class Iterator : public CIterator<CAnimal *> Declaring something a friend means it can access the private members of the enclosing class. This is being revised in the C++ standard. The new standard will be that a nested class has the same privileges as any other member of the class, so friend will not be necessary. Microsoft and GCC have already adopted the proposed standard. Some, like IBM, have not. In 11.8 [class.access.nest] paragraph 1, change: “The members of a nested class have no special access to members of an enclosing class, nor to classes or functions that have granted friendship to an enclosing class; the usual access rules (clause 11 [class.access]) shall be obeyed.” To: “A nested class is a member and as such has the same access rights as any other member. “
  • 40.
    40 An Example NestedClass class CActor { public: CActor(); //… class Bone { public: Bone(CActor *actor); //… private: std::wstring mName; Matrix mTransform; }; private: std::vector<Bone> mBones; }; Circle with + in it. Indicates Iterator is nested in CFarm 3
  • 41.
    41 Any more questions? classIterator : public CIterator<CAnimal *> { public: Iterator(CFarm *farm) {mFarm = farm; mIter = farm->mInventory.begin();} Iterator(Iterator &i) {mFarm = i.mFarm; mIter = i.mIter;} virtual void First() {mIter = mFarm->mInventory.begin();} virtual void Next() {mIter++;} virtual bool IsDone() {return mIter == mFarm->mInventory.end();} virtual CAnimal * Current() {return *mIter;} private: Iterator() {} CFarm *mFarm; std::list<CAnimal *>::iterator mIter; };
  • 42.
    42 Why is thisbetter? class Iterator : public CIterator<CAnimal *> { public: Iterator(CFarm *farm) {mFarm = farm; mIter = farm->mInventory.begin();} Iterator(Iterator &i) {mFarm = i.mFarm; mIter = i.mIter;} virtual void First() {mIter = mFarm->mInventory.begin();} virtual void Next() {mIter++;} virtual bool IsDone() {return mIter == mFarm->mInventory.end();} virtual CAnimal * Current() {return *mIter;} private: Iterator() {} CFarm *mFarm; std::list<CAnimal *>::iterator mIter; }; CIterator<CAnimal *> *iter = farm.GetInventory(); for(iter->First(); !iter->IsDone(); iter->Next()) { iter->Current()->DisplayAnimal(); } delete iter; What if I were to change how I store my animals from a list to a vector?
  • 43.
    43 A different version classIterator : public CIterator<CAnimal *> { public: Iterator(CFarm *farm) {mFarm = farm; mIter = farm->mInventory.begin();} Iterator(Iterator &i) {mFarm = i.mFarm; mIter = i.mIter;} virtual void First() {mIter = mFarm->mInventory.begin();} virtual void Next() {mIter++;} virtual bool IsDone() {return mIter == mFarm->mInventory.end();} virtual CAnimal * Current() {return *mIter;} private: Iterator() {} CFarm *mFarm; std::vector<CAnimal *>::iterator mIter; }; CIterator<CAnimal *> *iter = farm.GetInventory(); for(iter->First(); !iter->IsDone(); iter->Next()) { iter->Current()->DisplayAnimal(); } delete iter; No change in how client uses the iterator
  • 44.
    44 What if wechange to store the inventory this way Private in CFarm: // Number of animals int mCnt; // Array of animals CAnimal **mAnimals; class Iterator : public CIterator<CAnimal *> { public: Iterator(CFarm *farm) {mFarm = farm; mIter = farm->mInventory.begin();} Iterator(Iterator &i) {mFarm = i.mFarm; mIter = i.mIter;} virtual void First() {mIter = mFarm->mInventory.begin();} virtual void Next() {mIter++;} virtual bool IsDone() {return mIter == mFarm->mInventory.end();} virtual CAnimal * Current() {return *mIter;} private: Iterator() {} CFarm *mFarm; std::vector<CAnimal *>::iterator mIter; }; What to change in this?
  • 45.
    45 What if wechanged to store the inventory this way class Iterator : public CIterator<CAnimal *> { public: Iterator(CFarm *farm) {mFarm = farm; mItem = 0;} Iterator(Iterator &i) {mFarm = i.mFarm; mItem = i.mItem;} virtual void First() {mItem = 0;} virtual void Next() {mItem++;} virtual bool IsDone() {return mItem == mFarm->mCnt;} virtual CAnimal * Current() {return mFarm->mAnimals[mItem];} private: Iterator() {} CFarm *mFarm; int mItem; }; Private in CFarm: // Number of animals int mCnt; // Array of animals CAnimal *mAnimals;
  • 46.
    46 Why is thisbetter? ➢ A uniform iterator interface for many classes ➢ Does not expose internal data structures ➢ Not dependent on internal data structures
  • 47.
    47 Should we use… CIterator<CAnimal*> *iter = farm.GetInventory(); for(iter->First(); !iter->IsDone(); iter->Next()) { iter->Current()->DisplayAnimal(); } delete iter; CFarm::Iterator *iter = farm.GetInventory(); for(iter->First(); !iter->IsDone(); iter->Next()) { iter->Current()->DisplayAnimal(); } delete iter; or Either works. May be easier to read. We can use a copy constructor .
  • 48.
    48 Be sure todelete the iterator CIterator<CAnimal *> *iter = farm.GetInventory(); for(iter->First(); !iter->IsDone(); iter->Next()) { iter->Current()->DisplayAnimal(); } delete iter;
  • 49.
    49 Our XML example classXmlTag : public XmlNode { public: XmlTag(const std::wstring name); ~XmlTag(); //… private: XmlNode *mChildren; // Pointer to singly linked list of child nodes XmlAttribute *mAttributes; // Pointer to singly linked list of attributes }; class XmlNode { //… XmlNode *Next(); };
  • 50.
    50 An Alternative fromthe GoF book template <class T> class CIteratorPtr { public: CIteratorPtr(CIterator<T> * i) : mIter(i) {} ~CIteratorPtr() {delete mIter;} CIterator<T> *operator->() {return mIter;} CIterator<T> &operator*() {return *mIter;} private: CIteratorPtr() {} CIteratorPtr(const CIteratorPtr *) {} CIteratorPtr &operator=(CIteratorPtr &) {} CIterator<T> *mIter; }; This is called a pointer class. It holds a pointer to an iterator and takes care of deleting the iterator when the pointer object is destroyed. CIteratorPtr<CAnimal *> iter = farm.GetInventory(); for(iter->First(); !iter->IsDone(); iter->Next()) { iter->Current()->DisplayAnimal(); }
  • 51.
    51 The ever growingfarm example
  • 52.
    52 Some Caveats It shouldalways be clearly stated if an interator is invalidated by other operations, particularly add and remove. When in doubt, assume the interator is invalidated. It usually is! CIteratorPtr<CAnimal *> iter = farm.GetInventory(); for(iter->First(); !iter->IsDone(); iter->Next()) { if(iter->Current()->IsUgly()) farm.Remove(iter->Current()); } How might I do something like this so it will work?
  • 53.