Recall: Iterator Pattern

class List {
	struct Node;
	Node *theList;
public:
	class Iterator {
		Node *p;
	public:
		Iterator(Node *p) p {p} {}
		int &operator *() {return p->data;}
		bool operator!=(const Iterator &other) const {return p!=other.p;}
	};
	Iterator begin(){return Iterator {theList};}
	Iterator end(){return Iterator {nullptr};}
	...
};

[client]
List l;
l.addToFront(1);
l.addToFront(2);
l.addToFront(3);
// |-------------| auto
for(List::Iterator it = l.begin(); it != l.end(); ++it) {
	cout << *it << endl;
} // O(n)

Shortcut: range-based for heap

for(auto n:l) {
	cout << n << endl;
}

<aside> đź’ˇ Available for any class with

If you want to modify list items (or save copying)

for(auto &n:l) {
	++n;
}

<aside> 💡 Encapsulation ct’d

We could - make Iterator’s ctor private

Sol’n:

class List {
	...
public:
	class Iterator {
		Node *p;
		Iterator(Node *p);
	public:
		...
		...
		friend class List; 
		// List has access to all members of Iterator.
		// Node: doesn't matter where in class Iterator you put this
	};
}

Now List can still create Iterator, but client can only create Iterator by calling begin or end

Give your classes as few friends as possible - weakens encapsulation

Providing access to private fields: accessor/mutator methods

class Vec {
	int x, y;
public:
	...
	int getX()const {return x;} // accessor
	void setY(int x) {y = z;} // mutator
};

What about operator << - needs x, y but can't be a member

class Vec {
	...
public:
	friend ostream &operator<<(ostream &out, const Vec &v);
}; // ↑ non-member f'n

ostream &operator<<(ostream &out, const Vec &v){
	return out << v.x << ' ' << v.y;
}             // has access to Vec fields

<aside> đź’ˇ Equality Revisited

Suppose we wat to add a length() method to List How should we implement it?

Options

  1. Loop through the nodes & couting them: O(n)
  2. Store the length as a field & keep it up to date: O(1) length with negligible additional cost to addToFront → generally prefeered

But consider again the spaceship operator <=> in the special case of equality checking

l1 == l2 translates to (l1 <=> l2) == 0

What is the cost of <=> on two lists? O(length of shorter list)

But for equality checking, we missed a shortcut: lists whose lengths are different cannot be equal. In this case, we could answer “not equal” in O(1) time.

</aside>

class List {
	Node *theList;
public:
	auto operator<=>(const &other) const {
	if(!theList && !other.theList) return std::strong_ordering::equal;
	if(!theList) return std::strong_ordering::less;
	if(!other.theList) return std::strong_ordering::greater;
	return *theList <=> *other.theList;
	}
	// asking specifically length are equal
	bool operator==(const List &other) const {
	if(length != other.length) return false; // O(1)
	return (*this <=> other) == 0;
	}
};

Operator <=> gives automatic impl’s to all 6 relational operators, but if you write operator== separately, the compiler will use that for both == and != instead of <=> Let’s you optimize your equality checking if possible

<aside> đź’ˇ System Modelling

Visualize the structure of the system (abstractions & relationships among them) to aid design, implementation, communication

Popular standard: UML (Unified Modelling Language)

Modelling classes: -pricate, +public

IMG_2937.heic

graph TD
  Vec --> B("-x:Integer"<br> -y:Integer) 
			--> C("+getX:Integer"<br>+getY:Integer)

</aside>

<aside> đź’ˇ Relationship: Composiiton of classes

class Basis {
	Vec v1, v2;
};

Embedding one object(Vec) inside another(Basis) called composition

A Basis is composed of 2 Vecs. They are part of a Basis, and that is the only purpose of these Vecs


Relationships: a Basis “own a” Vec (in fact, it owns 2 of them)

If A “owns a” B, then typically,

eg. A car owns its engine - the engine is part of the car

Implementation: usually as composition of classes

</aside>

IMG_2938.heic

<aside> đź’ˇ Aggregation

Compare car parts in a car (”owns a”) vs. car parts in a catalogue

The catalofue contains the parts, but the parts exist on their own.

“has a” relationship(aggregation)

If A “has a” B then typically,

IMG_2939.heic

</aside>

#ifndef TIER_LIST_H
#define TIER_LIST_H
#include "list.h"
#include <string>

class TierList {
  List **tiers;
  size_t tierCount;
  size_t reserved;

  void swap(TierList &other);
  void enlarge();

 public:
  // Default constructor and destructor for a TierList.
  // The default constructor should initalize an empty tier list.
  TierList();
  ~TierList();

  // Adds/removes a tier at the end of the tier list.
  // Tiers are indexed starting at 0.  Runs in time
  // at most _linear in the number of tiers_, but _not_ in the number
  // of elements.
  void push_back_tier();
  void pop_back_tier();

  // Adds/removes an element at the front of the given tier.
  // Must run in constant time.
  void push_front_at_tier(size_t tier, const std::string &entry);
  void pop_front_at_tier(size_t tier);

  // Returns the number of tiers.  Runs in constant time.
  size_t tierSize() const;
  // Returns the number of elements.  Can run in time
  // up to linear in the number of tiers.
  size_t size() const;

 public:
  struct value_type {
    size_t tier;
    std::string entry;
  };
  class iterator {
    friend class TierList;

    // Fill in whatever private fields your iterator class needs.

    iterator(/* Fill in whatever arguments you need */);
  public:
    // Returns a value_type instance, containing
    // - a) the tier the item that the iterator points to lives at.
    // - b) the item the iterator points to
    value_type operator*() const;

    iterator &operator++();

    // New iterator operators which return a iterator pointing to the
    // start of the tier bk elements behind/fwd elements later
    // of the tier the iterator is currently on.
    //
    // If said tier is empty, the iterator moves back/forward to the next
    // non-empty tier.
    iterator operator<<(int bk) const;
    iterator operator>>(int fwd) const;

    bool operator!=(const iterator &other) const;
  };

  iterator begin() const;
  iterator end() const;
};

#endif
Node *theList = nullptr;
    iterator(Node *theList);