A comprehensive teaching guide covering memory model fundamentals through modern smart pointers.
new / delete and avoid common pitfallsunique_ptr, shared_ptr, weak_ptr) for safe resource managementOpen cpp-pointers-enhanced.html in any modern browser. No server or build step required.
| Key | Action |
|---|---|
← / → | Previous / Next slide |
Space | Advance to next slide |
T | Toggle Table of Contents overlay |
Exercise links on slides sE1 and sE2 open in new tabs automatically.
| File | Description | Est. Time |
|---|---|---|
cpp-pointers-enhanced.html |
Main slide deck (45 slides) | ~320 min across 4 sessions |
exercise-pointer-basics.html |
Exercise 1: Pointer tracing, debugging, swap function (8 problems) | ~25–30 min |
exercise-dynamic-memory.html |
Exercise 2: Heap tracing, bug hunting, DynArray, smart pointers (13 problems) | ~30–35 min |
Slides: s1, s2, s2b, s3, s3b, s4, s4b, s5, s5b, s6, s6b, s7, s8, s8b, sCA, sE1
| Slide | Title | Type | ~Min | Instructor Notes |
|---|---|---|---|---|
s1 | Pointers & Dynamic Memory | Title | 2 | Welcome, set expectations. Every variable has an address. |
s2 | What Is Memory? | Interactive | 4 | Click addresses to show value lookup. Apartment analogy. Emphasize stack vs heap distinction. |
s2b | How Variables Are Stored | Step-through | 4 | Step through each variable (int=4B, char=1B, double=8B, bool=1B). Mention sizeof. |
s3 | Your First Pointer | Interactive | 5 | Change x value with slider. Treasure map analogy: x=treasure, p=map, *p=follow map. |
s3b | Why Do We Need Pointers? | Lecture | 3 | Three reasons: avoid copies, dynamic data structures (linked list), polymorphism. |
s4 | Declaring & Using Pointers | Step-through | 5 | 4-step walkthrough: declare int, declare pointer, point to val, read through pointer. |
s4b | Pointer Syntax Gotchas | Lecture | 4 | Three traps: int* p, q (q is not pointer!), style wars, const pointer combos. Read right-to-left rule. |
s5 | The Dereference Operator (*) | Interactive | 4 | Set *p value interactively. Remote control analogy. |
s5b | Pointers & Function Parameters | Step-through | 5 | Broken swap (by value) vs working swap (by pointer). 4-step animation. |
s6 | Pointer Arithmetic & Arrays | Interactive | 5 | p++ advances, p-- retreats. Key: moves by sizeof(type), not 1 byte. |
s6b | Pointer Arithmetic Deep Dive | Interactive | 4 | Type selector (int/char/double) changes scaling visualization. |
s7 | nullptr | Lecture | 3 | nullptr vs NULL vs 0. Always check before dereferencing. |
s8 | References vs Pointers | Lecture | 4 | Comparison table. References can't be null, can't reassign. |
s8b | Pass by Reference vs Pointer | Lecture | 3 | When to use each. Reference = simpler, pointer = nullable/reassignable. |
sCA | Challenge A: Pointer Tracing | Challenge | 8 | 3 snippets. Students pick output. Discuss after. |
sE1 | Exercise 1 | Exercise | 25 | Open exercise-pointer-basics.html. Walk around. ~25–30 min. |
Slides: s9, s9b, s9c, s10, s10b, s11, s11b, sCB0, s12
| Slide | Title | Type | ~Min | Instructor Notes |
|---|---|---|---|---|
s9 | Stack vs Heap | Step-through | 5 | Animated stack frames. Key distinction: auto vs manual lifetime. |
s9b | Stack Frames in Detail | Interactive | 5 | Visualize function calls pushing/popping frames. |
s9c | When Stack Isn't Enough | Lecture | 4 | Three scenarios: runtime-sized data, outlive scope, large allocations. |
s10 | new and delete | Step-through | 5 | Show allocation/deallocation. Emphasize: every new needs a delete. |
s10b | new Can Fail | Lecture | 4 | std::bad_alloc, nothrow variant. Rare but important. |
s11 | Dynamic Arrays | Step-through | 5 | new int[n] and delete[]. Array vs single allocation. |
s11b | 2D Dynamic Arrays | Interactive | 5 | Array of pointers pattern. Nested allocation/deallocation. |
sCB0 | Challenge: Stack or Heap? | Challenge | 8 | 6 questions. Good formative assessment checkpoint. |
s12 | Memory Leaks | Interactive | 5 | Visualize leak. Growing memory usage graph. |
Slides: s13, s13b, s14, s14b, sCB, sE2, s15, s15b, s16, s16b
| Slide | Title | Type | ~Min | Instructor Notes |
|---|---|---|---|---|
s13 | Dangling Pointers & Double Free | Interactive | 5 | Two most dangerous bugs after leaks. |
s13b | Valgrind & AddressSanitizer | Lecture | 4 | Demo commands if time. Show sample output. |
s14 | Rule of Three | Lecture | 5 | If you need destructor, you need copy constructor + copy assignment. |
s14b | Shallow vs Deep Copy | Interactive | 5 | Toggle visualization. Critical for understanding ownership. |
sCB | Challenge B: Spot the Bug | Challenge | 8 | 4 bug identification questions. Great discussion opportunity. |
sE2 | Exercise 2 | Exercise | 30 | Open exercise-dynamic-memory.html. Parts A–D. ~30–35 min. |
s15 | Smart Pointers — RAII | Lecture | 4 | Transition to modern C++. "Resource Acquisition Is Initialization." |
s15b | What Is RAII? | Interactive | 5 | Lifecycle visualization. |
s16 | unique_ptr | Step-through | 5 | Exclusive ownership. Move semantics intro. |
s16b | unique_ptr Patterns | Lecture | 4 | Factory pattern, container usage, custom deleters. |
Slides: s17, s17b, s17c, s18, s18b, sCC, sQ1, sQ2, sQ3, sQ4
| Slide | Title | Type | ~Min | Instructor Notes |
|---|---|---|---|---|
s17 | shared_ptr | Interactive | 5 | Reference counting visualization. |
s17b | weak_ptr — Breaking Cycles | Lecture | 5 | Circular reference problem. Observer pattern. |
s17c | Smart Pointer Cheat Sheet | Reference | 3 | Quick comparison table. Good handout material. |
s18 | Key Takeaways | Lecture | 4 | Summary of all 4 sessions. |
s18b | Common Interview Questions | Lecture | 5 | Real interview prep. Engage students. |
sCC | Challenge C: Manual vs Smart | Challenge | 8 | 4 scenarios picking the right smart pointer. |
sQ1 | Quiz: Pointer Concepts | Quiz | 5 | 3 fundamental questions. |
sQ2 | Quiz: Memory Trace | Interactive | 8 | Step-through trace. No graded questions, just visual walkthrough. |
sQ3 | Quiz: Pick the Right Tool | Quiz | 5 | 4 scenario-based questions. |
sQ4 | Bonus Quiz: Mixed Bag | Quiz | 8 | 4 harder questions. Great for review. |
| Variable | Value |
|---|---|
a | 20 |
*p | 20 |
b | 25 |
*p = 20 changes a to 20 (p points to a). Then b = *p + 5 = 25.
| Variable | Value |
|---|---|
x | 20 |
y | 50 |
*p | 50 |
*q | 50 |
p==q | true |
*p = *q copies y's value (20) into x. p = q makes p point to y. *p = 50 changes y to 50. Both p and q now point to y.
| Variable | Value |
|---|---|
*p | 20 |
val | 60 |
p starts at arr[0], p++ moves to arr[1] (20). *(p+2) = arr[3] = 40. val = 20 + 40 = 60.
| Variable | Value |
|---|---|
a | 11 |
b | 4 |
c | 15 |
p→a: *p = 1+10 = 11. p→b: *p = 2*2 = 4. p→c: *p = 11+4 = 15.
Bug: p doesn't point to valid memory.
Fix:
int x;
int* p = &x;
*p = 42;
Or: int* p = new int; *p = 42;
Bug: &p gives address of the pointer itself, not the value it points to.
Fix: int y = *p;
Bug: First allocation (42) is never freed before reassignment.
Fix:
int* p = new int(42);
delete p;
p = new int(99);
delete p;
void swap(int* a, int* b) {
int temp = *a;
*a = *b;
*b = temp;
}
| Line | Heap | Stack |
|---|---|---|
| 1 | int(10) | p→heap |
| 2 | int(10) | p→heap, q→heap (same address) |
| 3 | int(20) | p→heap, q→heap |
| 4 | freed | p, q dangling |
After delete, both p and q become dangling pointers.
| Line | Heap | Stack |
|---|---|---|
| 1 | int(5) | a→heap |
| 2 | int(5), int(10) | a→5, b→10 |
| 3 | int(13), int(10) | a→13, b→10 |
| 4 | int(13) | a→13, b dangling |
| 5 | int(13) | a→13, b=nullptr |
*a = *b + 3 copies the VALUE (13), not the pointer. After delete b and b = nullptr, a still points to 13.
| Line | Heap | Stack |
|---|---|---|
| 1 | [?,?,?] | arr→heap |
| 2 | [10,20,30] | arr→heap |
| 3 | [10,20,30] | arr→heap, p→arr[1] |
| 5 | freed | arr, p dangling |
p = arr + 1 points to the second element (20). After delete[], the entire array is freed.
| Line | Heap | Stack |
|---|---|---|
| 1 | int*(→int(42)) | pp→heap |
| 3 | int*(dangling) | pp→heap |
| 4 | empty | pp dangling |
Two heap blocks: int* (pointing to int(42)). delete *pp frees the int, delete pp frees the int*. Both gone.
Bug type: Memory Leak
The early return skips delete[] data, leaking memory.
Fix: Add delete[] data; before the return, or restructure the logic.
Bug type: Dangling Pointer
val is destroyed when function returns; pointer becomes dangling.
Fix: Allocate on heap with new int(42) and return that.
Bug type: Double Free
a and b point to the same address; deleting both is a double free.
Fix: Only delete once, set the other to nullptr.
Bug type: Mismatched new/delete
new int[100] must be paired with delete[] arr, not delete arr.
Fix: delete[] arr;
DynArray(int cap) {
data = new int[cap];
size = 0;
capacity = cap;
}
~DynArray() {
delete[] data;
}
void push_back(int val) {
if (size == capacity) {
capacity *= 2;
int* newData = new int[capacity];
for (int i = 0; i < size; i++)
newData[i] = data[i];
delete[] data;
data = newData;
}
data[size++] = val;
}
void process() {
auto p = make_unique<int>(42);
cout << *p;
// no delete needed -- RAII handles cleanup
}
auto w = make_shared<Widget>();
componentA->use(w);
componentB->use(w);
// no delete needed -- last owner frees automatically
| Q | Answer | Explanation |
|---|---|---|
| Snippet 1 | c) 20 30 | a starts 5, *p=20 changes a to 20, then p moves to b, *p=30 changes b to 30. |
| Snippet 2 | d) 42 42 | p1 and p2 both point to x. *p2=42 changes x. So x=42 and *p1=42. |
| Snippet 3 | b) 2 2 0 | *p = *q copies the VALUE (2→a), but p still points to a and q to b. p != q. |
| Q | Code | Answer | Explanation |
|---|---|---|---|
| 1 | int x = 42; | Stack | Local variable → stack. |
| 2 | int* p = new int(10); — where is p? | Stack | The pointer p itself is on the stack. |
| 2b | int* p = new int(10); — where is *p? | Heap | The int(10) is allocated with new → heap. |
| 3 | string s = "hello"; | Stack (string object) | The string object lives on the stack (internal buffer may be on heap — hidden). |
| 4 | int arr[5]; | Stack | Fixed-size local array → stack. |
| 5 | int* arr = new int[5]; | Heap | new int[5] → array data is on the heap. |
| 6 | static int count = 0; | Neither (data segment) | Static variables are in the data segment — neither stack nor heap! |
| Q | Answer | Explanation |
|---|---|---|
| Bug 1 | a) Memory Leak | The early return skips delete[] arr — memory leak! |
| Bug 2 | c) Double Free | p and q point to same memory. Deleting both = double free! |
| Bug 3 | d) Wrong Delete | new[] must match delete[]. Using plain delete on array = wrong delete! |
| Bug 4 | b) Dangling Pointer | val is a local variable. Returning its address = dangling pointer! |
| Q | Scenario | Answer | Explanation |
|---|---|---|---|
| 1 | Single-owner file handle | a) unique_ptr | Single owner → unique_ptr. Auto-deletes when scope ends. |
| 2 | Cache shared by multiple components | b) shared_ptr | Multiple owners, shared lifetime → shared_ptr with reference counting. |
| 3 | Pass existing object to function | c) Reference (&) | Not owning, just observing → pass by reference. No allocation needed. |
| 4 | Factory returns to caller | a) unique_ptr | Factory returns ownership → unique_ptr. Caller gets exclusive ownership. |
| Q | Question | Answer | Explanation |
|---|---|---|---|
| 1 | What does &x give you? | b) The memory address of x | & is the "address-of" operator. |
| 2 | What is *p when p is a pointer? | b) The value at the address p stores | * is the dereference operator. |
| 3 | What is nullptr? | b) A special value meaning "points nowhere" | Type-safe (unlike C's NULL). |
| Q | Scenario | Answer | Explanation |
|---|---|---|---|
| 1 | Modify caller's variable | d) Reference (&) | Just modifying — use a reference. Simplest, no ownership. |
| 2 | Linked list node→next | b) unique_ptr | Each node exclusively owns next → unique_ptr for clear ownership. |
| 3 | Texture shared by game objects | c) shared_ptr | Multiple objects share one texture → shared_ptr with ref counting. |
| 4 | Factory creates and returns | b) unique_ptr | Factory creates and transfers ownership → unique_ptr. |
| Q | Question | Answer | Explanation |
|---|---|---|---|
| 1 | What prints? (reference + pointer) | b) 10 10 | r is alias for x, &r == &x. *p=10 changes x. Both x and r are 10. |
| 2 | How many bytes leaked? | c) 40 bytes | a=b loses pointer to first array (10 ints x 4B = 40B leaked). Second array properly deleted. |
| 3 | Is auto p=make_unique, auto q=move(p), cout<<*p safe? | b) No, p is nullptr after move | After move, p is nullptr. Dereferencing = UB/crash. |
| 4 | What's wrong with foo(x) where foo takes unique_ptr by value? | b) Compile error: can't copy unique_ptr | unique_ptr can't be copied. Need foo(std::move(x)). |