Mini Project 7 — Playlist Ownership¶
Difficulty: Intermediate · Estimated effort: 4–5 hours ·
Prerequisites: Module 9 — Composition and Association,
Module 11 — RAII and Smart Pointers ·
Combines: owning vs observing raw pointers, unique_ptr, composition, an ownership diagram
No complete solution is provided. The design question — who owns a track — is the whole project, and answering it for you would remove the work.
1. Scenario¶
A community radio station keeps a library of tracks and builds programmes from them. The same track can appear in several programmes at once, a programme can be deleted without losing the track, and the library is cleared only when the station closes for the night.
The station's previous program kept every track in one global vector, which meant deleting a programme sometimes deleted a track another programme was still using.
2. Problem statement¶
Build a track library and a set of playlists over it. Decide, and express in types, who owns a track and who merely refers to it — then demonstrate that deleting a playlist cannot damage the library, and that closing the library cannot damage a playlist that still refers to it.
3. User requirements¶
- [ ] Add tracks to the library; the library keeps them.
- [ ] Create playlists that refer to tracks from the library.
- [ ] The same track may appear in more than one playlist.
- [ ] Delete a playlist and confirm the library is unchanged.
- [ ] Print a playlist, with each track's details.
4. Functional requirements¶
- [ ]
Trackwith title, artist and duration. - [ ]
Libraryowning its tracks viastd::unique_ptr. - [ ]
Playlistholding non-owning references to tracks. - [ ] A way to look a track up in the library by identity, not by copying it.
- [ ] Removing a track from the library is refused, or leaves every playlist consistent — you choose, and you document the choice.
- [ ]
Libraryis not copyable; it is movable, or not, by a documented rule.
5. Non-functional requirements¶
- [ ] Only one owner per track. A track destroyed twice is the bug this project exists to prevent; show that it cannot happen.
- [ ] No
newordeletein your own code. - [ ] Builds warning-free with
g++ -std=c++17 -Wall -Wextra -Werror. - [ ] Nothing outside the library frees or outlives the library's tracks.
- [ ] Every raw pointer in the code is a clearly-labelled observer.
6. Suggested features¶
- Playlist ordering: append, insert, move a track up or down.
- Total duration of a playlist, in minutes and seconds.
- A playlist of playlists ("the evening programme"), and a comment on what it owns.
- A
Trackshared by the library and a separate cache, if you want to practiseshared_ptr— with a written answer to which of the two owners is authoritative.
7. OOP concepts involved¶
| Concept | Where it appears |
|---|---|
| Composition | The library owns its tracks; a track's lifetime ends with the library's |
| Aggregation | A playlist refers to tracks that outlive it and that it does not own |
| Ownership in types | unique_ptr for owning, Track* for observing, and no ambiguity |
| Smart pointers | The default in this project is unique_ptr, not shared_ptr; justify any use |
| Copy control | Copying a library would duplicate every track; deciding is part of the design |
| Diagram literacy | An ownership diagram you draw, marking multiplicities and diamonds |
8. Suggested design considerations¶
Draw the ownership diagram before writing code. Two boxes and three arrows will do. If you cannot say who dies first, the design is not ready — and the compiler will let you build it anyway.
Reach for shared_ptr last. Shared ownership means "nobody can say who should keep this
alive". Here the answer is knowable: the library owns tracks, playlists borrow them. Use the
type that encodes that answer.
Decide what makes a track unique. A title is not an identity — two different recordings can share one. Give tracks an identifier and say what it means.
Handle the removal case explicitly. If the library drops a track that a playlist still refers to, something must happen. Choose: refuse, remove from every playlist, or a lazy validity check. Each is defensible; an unhandled dangling reference is not.
9. Minimum viable version¶
Track, Library owning vector<unique_ptr<Track>>, Playlist holding observers, lookup by
identifier, and a main that prints a playlist, deletes it, then prints the library to show it
is intact.
10. Intermediate extension¶
Add playlist reordering and total duration, then break your design on purpose: destroy the library first while a playlist still exists, and either show that it is impossible (by making the playlist hold a reference to the library) or document precisely what a user must not do.
11. Advanced extension¶
Add a second consumer of tracks — a "recently played" history — and convert one part of the
design to shared_ptr. Then answer in writing: what did shared ownership buy, what did it cost,
and could the same problem have been solved by strengthening the ownership rule instead?
12. Testing checklist¶
- [ ] A track added once appears once and has a unique identity
- [ ] Adding the same track to two playlists does not copy it — verified with a copy counter
- [ ] Deleting a playlist leaves every library track alive and unchanged
- [ ] Deleting a playlist does not affect another playlist sharing a track
- [ ] A track identifier that does not exist is refused rather than guessed at
- [ ] Copying the library is a compile error (or behaves as documented)
- [ ] At program end, each
Trackis destroyed exactly once — verified with a counter
13. Documentation checklist¶
- [ ] The ownership diagram, drawn and checked against the declarations
- [ ] One sentence per class saying what it owns
- [ ] The rule for removing a track, with the reason
- [ ] Why
unique_ptrhere rather thanshared_ptr - [ ] What a copy of a library or playlist would mean, and why it is allowed or forbidden
14. Self-evaluation rubric¶
| Criterion | Excellent looks like | Weight |
|---|---|---|
| Ownership design | Single, unambiguous owner; observers clearly marked; diagram matches code | 35% |
| Lifetime safety | Destroyed exactly once; deletion orders cannot break the design | 25% |
| Interface | Lookups return observers, not copies; identifiers meaningful | 15% |
| Failure handling | Removal and missing-identifier cases decided and tested | 15% |
| Documentation | Diagram plus the reasoning, including what you rejected | 10% |
Related: Mini projects · Project roadmap · All projects · Module 11 — RAII and Smart Pointers