Lecture 13
Recall:
encapsulated list class
can't traverse the list node-to-node
repeatedly calling ith => O(n2) time
SE Topic: Design Patterns
certain programming problems arise often
keep track of good solutions to these problems reuse and adapt them
If you have this problem, then this technique may solve it
Our problem: traversing a list without resulting in O(n2) time while keeping nodes secure
Solution: Iterator Pattern
Iterator Pattern
Create a class that manages access to nodes
Abstraction of a pointer
Walk the list without exposing the actual pointers
Iterating through an array in C-style
for (int *p = arr; p < arr + size; ++p) {
cout << *p << endl;
}
Want class that acts as pointer p
class List {
struct Node;
Node *theList;
public:
class Iterator {
Node *p; // think of pointer as your bookmark (where am I in the array)
public:
explicit Iterator (Node *p): p{p}{}
Iterator &operator++() {
p = p->next;
return *this;
}
int &operator*() {
return p->data;
} // O(1)
bool operator!= (const Iterator &other) {
return p != other.p;
} // O(1)
};
Iterator begin() {
return Iterator{theList};
} // O(1)
Iterator end() { // O(1)
return Iterator{nullptr;}
}
}
Now what does client's code look like?
int main() {
List l;
l.addToFront(1);
l.addToFront(2);
l.addToFront(3);
for (List::Iterator it=l.begin(); it != l.end(); ++it) {
cout << *it << endl;
} // Now this runs in O(n) time instead of O(n^2)
}
Shortcut: automatic type deduction
Automatic Type Deduction
// gives x the same type as the value of y
auto x = y;
Applying the shortcut:
for (auto its=l.begin(); l != l.endl(); ++it) {
cout << *it << endl;
}
An even shorter shortcut:
// bi-value declaration: n is a copy of the list item
for (auto n:l) {
cout << n << endl;
}
// If you want to modify list items or save copy
for (auto &n:l) {
++n;
}
// OR
for (const auto &n:l) {
...
}
Range Loops
available for any class with:
methods
begin
andend
that produce iteratorsthe iterator must support
!=
, prefix++
, unary*
Encapsulation continued
Encapsulation with Constructors
List client can create iterators directly by doing:
auto it = List::Iterator {nullptr};
Violates encapsulation
client should be using end()
We could make Iterator's constructor private, but two problems arise:
Client cannot call
List::Iterator
List cannot call
Iterator
Problem: How do we allow client to create iterators without breaking encapsulation? Solution: Make List
a friend of Iterator
Friends
Gives class privileged access to another class
Solution explained:
Make List a friend
give List privileged access to Iterator by making List a friend
So now List can still create iterators, but client can only create iterators by calling begin/end
class List {
...
public:
class Iterator {
Node *p;
explicit Iterator(Node *p);
public:
...
friend class List; // List has access to all members of Iterator
};
...
};
Rule of Thumb with Friends: Give your classes as few friends as possible - weakens encapsulation
Accessor/Mutator Methods
Provides access to private fields
class Vec {
int x,y;
public:
...
int getX() const { // accessor
return x;
}
void getY(int z) { // mutator
y=z;
}
};
Why not just put int x and y in public?
advantage to this is if you want x and y to have certain restrictions, but not complete restriction
Dealing with private fields for theoperator<<
?
operator<<
?operator<< needs to access x and y fields, but the operator function cannot be a member (needs to be outside the class)
We have two options in this case:
if
getX
,getY
defined, then use these methods to access x and yif you don't want to provide getX and getY - make
operator<<
a friend function:
// .h
class Vec {
...
friend std::ostream &operator<< (std::ostream &out, const Vec &v);
// not a member function, this is a friend declaration (still has two arguments, one of them do not become this)
}
// .c
ostream &operator<<(ostream &out, const Vec &v) {
return out << v.x << ' ' << v.y;
}
System Modelling (UML)
Visualizing the structure of the system (abstractions and relationships among them) to aid design and implementation Popular Standard: UML (Unified Modelling Language)
Modeling Classes
Name
**Vec
Fields (optional)
-x: Integer, -y: integer
Methods (optional)
+getX: Integer, +getY: Integer
**Visibility: - means private, + means public, # means protected
Relationship: Composition of classes
class Vec {
int x,y,z;
public:
Vec(int, int, int);
};
// Two vecs define a plane:
class Plane {
Vec v1, v2;
...
};
// This does not compile
Plane p; // can't initialize v1 and v2 without help - no default constructor for vec
class Plane {
Vec v1, v2;
public:
// means initialize v1 to Vec {1,0,0};
Plane(): v1{1,0,0}, v2{0,1,0}, v3{0,0,1} {}
...
}
Composition (OWNS-A)
Embedding an object inside another
Example:
Embedding Vec inside Plane is called composition.
Relationship shown in example:
A Plane OWNS-A Vec (Owns 2 of them)
If A OWNS-A B, then typically: B has no identity outside A (no independent existence)
If A is destroyed, B is destroyed
If A is copied, B is copied (deep copy)
eg. A car owns four wheels - a wheel is part of a car
destroy the car => destroy the wheels
copy the car => copy the wheels
Typical Implemention: typically as composition of classes
Modelling: Plane
UML
Last updated
Was this helpful?