CS247 Lecture 14
Last Time: Exception safety, RAII, Smart pointers This Time: Shared_ptrs, Decorator Pattern, Observer Pattern
What if we want shared ownership, where same memory is deleted only if all the pointers pointing to it are deleted?
We can achieve this via std::shared_ptr<T>.
shared_ptr maintains a reference count, which keeps track of how many shared pointers point at this memory. When it hits 0, memory is freed.
{
// replace ... with args to create a C object
shared_ptr<C> p = make_shared<C>(...);
if (...) {
shared_ptr<C> q = p;
} // destructor for q runs, reference count is 1
...
} // dtor for p runs, ref count is 0, C object is deletedShared Pointers are not perfect.
There are still problems with shared_ptrs:
- Susceptible if you use the constructor
C* cp = new C{...};
shared_ptr<C> p{cp}; // Incorrect
shared_ptr<C> q{cp}; // Double deleteqhas no knowledge ofp’s existence
Danger
The problem here is that you’re using the constructor of
std::shared_ptrto initialize bothpandqwith the same raw pointercp. This means bothpandqthink they own the same object. When bothpandqgo out of scope, they will each try to delete the object, leading to a double delete and undefined behaviour.To avoid this issue, you should construct
std::shared_ptrusingstd::make_sharedor by directly passing the constructor arguments:
shared_ptr<C> p = make_shared<C>(...);
shared_ptr<C> q = p; // No double delete, shared ownership is managed correctly- Cyclical reference issue
- If we have multiple shared pointers that point at each other, we have the issue that some pointers may never be deleted
class Node {
shared_ptr<Edge> myEdges;
shared_ptr<Node> neighbors;
};myEdges: Thisshared_ptrpoints to a collection ofEdgeobjects associated with the currentNode. TheseEdgeobjects might also contain references to otherNodeobjects, creating a graph-like structure.neighbors: Thisshared_ptrpoints to otherNodeobjects that are considered neighbors of the currentNode. This establishes a connection between differentNodeinstances in the graph.

full example through ChatGPTt:
class Node {
public:
shared_ptr<Node> next;
};
int main() {
shared_ptr<Node> node1 = make_shared<Node>();
shared_ptr<Node> node2 = make_shared<Node>();
// Create a cyclical reference
node1->next = node2;
node2->next = node1;
return 0;
}In general, shared ownership is somewhat rare. Take care in constructing your programs. Usually, it suffices to use the following:
unique_ptror object field - ownership/Composition- raw pointer or reference - Aggregation/has-a
shared_ptr- shared ownership
Exercise: Implement shared_ptr CS247 final practice
We’ll come back to exception safety later.
- Provide some standard “good” solution to a common problem
- One common problem: adding / removing behaviour at run-time with a polymorphic class
- One example: Windowing system - GUI

If we attempt to make a class for each possible feature, for features, there are possible configurations. Combinatorial explosion!
Solution: Decorator Pattern (Linked List of functionalities)

Abstract Component - Gives the interface for our classes Concrete Component - Implements the “basic” version of the interface
Abstract Decorator - Organize decorators and has-a decorator or the concrete component.
Concrete Decorators - Implement the interface, call operation() on the next object in this linked list.

Window* w = new ScrollBar{new Tabbing{new BasicWindow{}}};ScrollBar Tabbing Basic Window
w->render()Example: Pizza

class Pizza {
public:
virtual ~Pizza(){}
virtual float price() const = 0;
vritual string desc() const = 0;
}
class CrustAndSauce: public Pizza {
public:
float price() const override {return 5.99;};
string desc() const override {return "pizza";};
}
class Decorator: public Pizza {
protected:
Pizza* next;
public:
Decorator(Pizza* next): next{next} {}
~Decorator() {delete next;}
}
class Topping: public Decorator {
string name;
public:
Topping(const string& s, Pizza* p): Decorator{p}, name{s} {}
float price() const override {
return 0.75 + next->price();
}
string desc() const override {
return next->desc() + " with" + name;
}
}
class StuffedCrust: public Decorator {
public:
StuffedCrust(Pizza* p): Decorator{p} {}
float price() const override {
return next->price() + 2.50;
}
string desc() const override {
return next->desc() + " with stuffed crust";
}
}
Pizza* p = new CrustAndSauce{};
p = new Topping{"cheese", p};
p = new Topping{"pepperoni", p};
p = new StuffedCrust{p};
cout << p->price() << " " << p->desc() << endl;
delete p;Next: CS247 Lecture 15