Skip to content

Mini Project 4 — Visitor Counter

Difficulty: Beginner · Estimated effort: 2 hours · Prerequisites: Module 5 — Operators and Static Members · Combines: static members, class-level state, the Rule of Zero

No complete solution is provided. This is a short project, so the checklist is short too — which is not the same as easy.


1. Scenario

A small museum has a single turnstile and no budget for a system. The staff want a program that models the gallery: how many people are inside right now, how many have entered since it opened, and the maximum that were inside at any one time. When the building closes with anyone still inside, that is an incident worth recording.


2. Problem statement

Build a VisitorCounter type that tracks the number currently inside and the running totals, plus a Visitor type whose construction and destruction drive it — so that "someone entered" and "someone left" are properties of a visitor's lifetime rather than calls a programmer remembers to make.


3. User requirements

  • [ ] See how many are inside right now.
  • [ ] See the total entries for the day and the peak occupancy.
  • [ ] Have entry and exit recorded automatically when a visitor arrives or leaves.
  • [ ] Get a warning at closing time if anyone is still inside.
  • [ ] Have the counters reset for a new day without restarting the program.

4. Functional requirements

  • [ ] VisitorCounter::inside(), total(), peak() — read-only queries.
  • [ ] A Visitor object whose construction increments and whose destruction decrements.
  • [ ] Peak occupancy updated on every entry.
  • [ ] A reset() that clears the day's numbers and reports how many were still inside.
  • [ ] Automatic cleanup demonstrated on a path that leaves early (an early return).

5. Non-functional requirements

  • [ ] The shared state is static and private, with no public counters to poke at.
  • [ ] Visitor is not copyable — a copy would double-count, and the compiler should refuse.
  • [ ] No manual enter()/exit() calls anywhere in main; lifetimes do the work.
  • [ ] Builds warning-free with g++ -std=c++17 -Wall -Wextra -Werror.
  • [ ] The type follows the Rule of Zero or states why it does not.

6. Suggested features

  • A per-hour count, using a small array indexed by hour, printed as a simple histogram.
  • A Ward type that owns a group of visitors and reports its own occupancy.
  • Move semantics for Visitor, with the counting rules for moved-from objects written down.

7. OOP concepts involved

Concept Where it appears
Static members The counters are class-level, shared by every visitor
Constructors / destructors Entry and exit are the object's lifetime, not a remembered call
RAII as a counting device Correct counting follows from correct lifetimes
Deleted functions Visitor(const Visitor&) = delete prevents the double-count bug
Encapsulation Nothing outside can set a counter to a wrong value

8. Suggested design considerations

Ask what should happen when a Visitor is copied. The intuitive answer, "count it twice", is wrong: a copy is a second name for the same visit, not a second visit. Deleting the copy constructor turns a run-time miscount into a compile error, which is the better bug.

Separate the counter from the visitor. VisitorCounter holds the shared state; Visitor holds nothing but its own identity. If a visitor starts holding a copy of the totals, the totals can disagree.

Decide where the peak is computed. Recomputing it at query time is simpler; tracking it on entry is cheaper and easier to test. Either is fine if it is chosen on purpose.


9. Minimum viable version

VisitorCounter with the three queries, a reset(), and Visitor with a deleted copy constructor. A main that creates visitors in a loop, destroys some early, and prints the totals.


10. Intermediate extension

Add a scope in main containing visitors that are destroyed early, and demonstrate that the "inside" count returns to the correct value when the scope ends — including when the scope is left through an early return. Then remove the deleted copy constructor deliberately and observe whether your tests would have caught the double-count.


11. Advanced extension

Add move semantics and write down the rule for a moved-from visitor: does it still hold a place in the room? Implement whichever answer you choose, then test the sequence move → destroy → destroy, and check that the count matches your documented rule rather than your intuition.


12. Testing checklist

  • [ ] Constructing n visitors reports n inside and n total
  • [ ] Destroying one reports n − 1 inside and n total
  • [ ] Peak is the highest inside value ever seen, not the final one
  • [ ] An early return from a scope still decrements
  • [ ] reset() reports the number left inside and zeroes the day
  • [ ] Attempting to copy a Visitor fails to compile, and you have the diagnostic recorded
  • [ ] A second day's counts do not include the first day's numbers

13. Documentation checklist

  • [ ] A header comment explaining that counting is driven by lifetimes
  • [ ] Why copying is deleted, in one sentence
  • [ ] The reset semantics: what is cleared, what is reported
  • [ ] The move rule, if you implemented move semantics
  • [ ] A note saying the counters are not thread-safe

14. Self-evaluation rubric

Criterion Excellent looks like Weight
Correct accounting Counts are right on the normal and the early-return path 30%
Design Shared state in one class; visitors hold no totals 25%
Compile-time safety Copying is impossible, not merely discouraged 20%
Interface Queries are const; nothing exposes a settable counter 15%
Documentation Moved-from and copy rules written down 10%

Related: Mini projects · Project roadmap · All projects · Module 5 — Operators and Static Members