Practice Lab 10 — Warehouse bin with class-level barcodes¶
Difficulty: Intermediate · Estimated time: 4 hours · Module 5: Static Members, Friend Functions and Operator Overloading
Class-level state is easy to use and hard to use well. This lab makes you find its edges: sharing, initialisation order, and the fact that a static member is nobody's in particular.
At a glance¶
| Difficulty | Intermediate |
| Estimated time | 4 hours |
| Module | Module 5 — Static Members, Friend Functions and Operator Overloading |
| Concepts practised | Static data members, static member functions, class-level state, initialisation, friend functions and classes, comparison operators for containers |
| Prerequisite | Practice Lab 9 |
| Class design | partly yours — the bin type's rules are given, the members are not |
Scenario¶
A warehouse has bins on racks. Every bin gets a barcode when it is created, and the barcode is unique across the whole warehouse. The warehouse also keeps a count of how many bins exist, and the same count is reported by a single office report rather than by every caller adding up its own list.
Bins are also sorted, printed on labels, and compared for identity on a scanner, so the bin type needs the operators the standard library expects.
The previous attempt kept the next barcode in a global variable and the bin count in another one. Two translation units each had their own copy of one of them, and forty bins ended up sharing nineteen barcodes. That is what this lab is for.
Problem statement¶
Implement:
Bin— a barcode, a rack code, a capacity and a current content count. Operations:storeandtake, each respecting the capacity, plusdescribe.- A class-level barcode source: each new bin receives the next barcode, unique across the program, and the counter is not a global.
- A class-level count of live bins, with a static query, which is exact after objects are destroyed.
- Operators:
==and!=for identity (barcode),<for sorting by barcode, and<<for a label. - A friend that needs access your public interface does not provide — and you must justify it in
static-notes.md. (If you conclude no friend is needed, that is a defensible answer: write the justification for the friend you considered and rejected.) Rack— holds bins in a fixed array, refuses duplicates, and prints its bins in barcode order.
Then, in static-notes.md:
- Where the barcode counter is defined, in which file, and what would break if it were defined in a header instead (say what the linker would say).
- What the live-bin counter would report if a
Binwere copied, and what that means for your design. - Why the barcode is not copied with the bin, or what happens if it is.
Learning objectives¶
By the end of this lab you will be able to:
- Declare and define a static data member correctly across files, and explain the one-definition rule that applies to it.
- Use a static member function as a class-level query, and say why it cannot be a non-static one.
- Design an identity that is assigned by the class rather than supplied by the caller.
- Use
friendwhere it is justified, and explain what it gives up. - Explain how copying interacts with a class-level counter, and choose the behaviour you want.
Functional requirements¶
- Barcodes are unique for every bin created, in creation order, and never reused.
- Barcodes survive a copy: copying a bin produces a bin with the same barcode, and the live count does not increase — or a copy is refused. Choose one, implement it, and justify it.
- The live-bin count is exact at every point where
mainprints it. storerefuses zero, negative, and more than the remaining capacity, naming both numbers.takerefuses zero, negative, and more than the current content.describeincludes the barcode, the rack code, the content and the capacity.Rack::addrefuses a duplicate barcode and a 21st bin, with messages.Rack::printprints in ascending barcode order.- The label operator prints in a fixed format, one line.
- A
const Bin&can be printed and compared.
Constraints¶
- Header plus source file for
Bin, header plus source file forRack, andmain.cpp; explicit file list:g++ -std=c++17 -Wall -Wextra -Werror bin.cpp rack.cpp main.cpp -o warehouse - No global variables. The barcode counter and the live count are static members.
- No inheritance, no templates, no smart pointers.
- The label operator is a free function (it is), and any
friendmust be justified in writing. - No warnings, including for the unused friend you decided against.
Starter task¶
In static-notes.md, before coding:
- Where will the counter's definition live? Write the two lines — declaration and definition — and say which file each goes in.
- Copy a bin in your head. What should happen to the live count, and what should the copy's barcode be? Write the rule, then the code that enforces it.
- Which operations can be static, and which cannot? Take
describeand the counter query, and explain the difference in one sentence each. - Name the friend you are considering, and what it needs that your public operations do not provide.
Implementation tasks¶
- Implement
Binwith the static counter, the barcode assignment and the capacity rules. - Implement the operators, including the label.
- Implement
Rack. - Write
main: create bins, copy one, destroy a scope, print the live count at three points, and print a rack in barcode order. - Run every test case and record the output, including the live count at each point.
- Move the counter's definition into the header, try to build, and record the exact message before you move it back.
Test cases¶
| # | Action | Expected result |
|---|---|---|
| 1 | Create three bins | Barcodes 1, 2, 3 in creation order |
| 2 | Copy a bin, print both | Same barcode; live count unchanged (or the copy is refused) — per your rule |
| 3 | Create a bin inside a scope, leave it | Live count returns to its previous value |
| 4 | store more than the remaining capacity |
Refused, naming required and available |
| 5 | store exactly to capacity |
Accepted; content equals capacity |
| 6 | take more than the content |
Refused |
| 7 | Add a duplicate barcode to a rack | Refused |
| 8 | Add a 21st bin to a rack | Refused |
| 9 | Print a rack with bins added out of order | Printed in barcode order |
| 10 | Compare two bins with == |
true only when the barcodes match |
| 11 | Print a const Bin& |
Compiles, fixed label format |
| 12 | Move the counter definition to the header and build | A link failure; the message is recorded verbatim |
Boundary cases to run deliberately: a bin with capacity 1; a rack with one bin; two bins created in different functions.
Expected behaviour¶
Bin 1 rack A1 content 0/10
Bin 2 rack A2 content 0/5
Bin 3 rack B1 content 0/1
copy of Bin 1 keeps barcode 1
live bins: 3
bin in a scope created as barcode 4
live bins: 3
rack A: [Bin 1 A1 0/10] [Bin 2 A2 0/5]
store Bin 1 10 -> accepted, content 10/10
store Bin 1 1 -> refused: requested 11, capacity 10
Extension challenge¶
Make barcodes recover after a restart: the warehouse can be told the last barcode it issued, and the next creation continues from there.
Record:
- How the value enters the program, and where it is validated (a barcode that has already been used must not be reissued).
- Whether the counter belongs to the class, to a warehouse object, or to a small factory — with a one-sentence cost for each.
- What your live-count rule does when bins are loaded from a file rather than constructed by hand.
- The change cost: files, operations, lines.
Debugging challenge¶
The barcode counter reports 1 every time it is queried from another file:
// bin.h
class Bin {
public:
static int issued(); // declared here
static int next_barcode_; // declared here
// ...
};
// bin.h, later, by accident:
static int Bin::next_barcode_ = 0; // "initialise it here, next to the class"
Reproduce it — call issued() from two translation units and compare — and answer:
- Why did each translation unit see its own counter, and what does
staticat namespace scope do to a definition? - Why is this not a duplicate-definition error, and what makes it worse than one?
- Write the correct declaration and definition, in the correct files, and say why the definition cannot be
in the header (unless you make it
inline— check whether that fixes it, and record what you find).
Reflection questions¶
- Your counter is shared by every bin. In one sentence, who owns it, and what does that mean for the program's structure?
- Copying a bin and the live count interact. What did you choose, and what would a caller who copies bins for a report now see?
- Which of your operations is static, and which could have been? What decides it?
- You justified, or rejected, a friend. What did that decision reveal about your public interface?
- The rack compares bins by barcode. What does that ordering mean for two bins with the same rack code?
- If the warehouse ran as two processes at once, what would go wrong with this design, and what would you need instead? (One sentence: the answer is Module 17's subject, which this course does not have.)
Completion checklist¶
- [ ] It builds from a documented command with
-Wall -Wextra -Werrorand no warnings - [ ] No globals; both counters are static members with a correct declaration/definition split
- [ ] The link failure from a header definition was reproduced and its message recorded
- [ ] Barcodes are unique in creation order
- [ ] The live count is exact at all three printed points, and the copy rule is enforced
- [ ] Capacity rules refuse, naming both numbers
- [ ]
==,<and<<behave as specified - [ ] A
const Bin&can be printed and compared - [ ] All twelve test cases run, with output recorded
- [ ]
static-notes.mdstates where each counter lives and why copying behaves as it does - [ ] The debugging challenge was explained before being fixed
Related: Lab index · Module 5 — Operators and Static Members · Previous: Practice Lab 9 · Next: Practice Lab 11 — Vehicle hierarchy