Skip to content

Mini Project 2 — Saving Account

Difficulty: Beginner · Estimated effort: 3 hours · Prerequisites: Module 4 — Encapsulation, Module 3 — Constructors and Destructors · Combines: encapsulation, invariant enforcement, constructors, static bookkeeping

No complete solution is provided. Build it from the requirements, then review it against the checklists below.


1. Scenario

A savings scheme for a small club needs a program that tracks each member's balance. The rules are simple but absolute: a balance can never go negative, a withdrawal larger than the balance is refused rather than partly applied, and every account belongs to a named member with an account number the club can read over the phone.

The previous semester's version stored balances in a global array and let a withdrawal push one negative. The club noticed three weeks later.


2. Problem statement

Build an Account type whose balance is private, whose operations either succeed completely or change nothing, and whose account numbers are unique by construction rather than by convention.


3. User requirements

  • [ ] Open an account for a named holder; the club never supplies the number.
  • [ ] Deposit and withdraw, with a refusal that says why.
  • [ ] Transfer between two accounts without ever borrowing from an account that is not there.
  • [ ] Print a statement line: number, holder, balance.
  • [ ] See how many accounts currently exist.

4. Functional requirements

  • [ ] Private balance, holder name and account number.
  • [ ] deposit(amount) rejects a non-positive amount.
  • [ ] withdraw(amount) rejects a non-positive amount and an amount above the balance, and leaves the balance untouched in both cases.
  • [ ] transferTo(other, amount) leaves both accounts untouched if any part of it fails.
  • [ ] Account numbers are sequential and unique, generated by the class.
  • [ ] operator<< prints the account.

5. Non-functional requirements

  • [ ] Money is stored in integer minor units (pence/cents), never double.
  • [ ] No public data members and no setters that bypass the rules.
  • [ ] Builds warning-free with g++ -std=c++17 -Wall -Wextra -Werror.
  • [ ] A failed operation changes nothing that can be observed afterwards.
  • [ ] No global balance state; the class is the only thing that touches a balance.

6. Suggested features

  • A daily withdrawal limit, stored per account, enforced on the same code path.
  • An optional overdraft for accounts opened with a limit — and a decision about whether it belongs in this class or in a derived one.
  • A frozen flag: transfers out are refused, deposits still allowed.
  • A short transaction log of the last n operations, printed with the statement.

7. OOP concepts involved

Concept Where it appears
Encapsulation The balance is only ever changed through a validating operation
Invariant balance >= 0 holds after every operation, or the type is wrong
Constructors An account cannot be created with a negative opening balance
Static members The next account number, and the live account count
Strong guarantee A failed transfer leaves both accounts unchanged
Copy control Decide deliberately what copying an account means, and write it down

8. Suggested design considerations

Ask what a transfer guarantees. The natural implementation subtracts from one account and adds to the other. If the addition cannot fail, that is fine — but say so. If it can, the order of operations matters and the guarantee has to be designed, not hoped for.

Compile the invariant, do not document it. A private balance plus two validating member functions is stronger than a comment that says "balance must not go below zero".

Static counters are still state. The next account number is shared by every account, so think about what happens when accounts are copied and destroyed, and about whether the count should be per-object or per-program. Both answers can be right; an unexamined one cannot.

Resist the setter. setBalance will be tempting within an hour of starting. Almost every bug this project is designed to prevent begins with one.


9. Minimum viable version

Account with private members, deposit, withdraw, transferTo, balance(), holder(), number(), and an operator<<. A main that performs a handful of operations, prints the balance after each, and attempts two operations that must be refused.


10. Intermediate extension

Add the transaction log and print it with the statement. Then verify the guarantee under failure: attempt a transfer that must be refused and print both balances afterwards, showing that neither moved.


11. Advanced extension

Add a second kind of account — one with an overdraft limit — using inheritance, and make the withdrawal rule virtual. Then answer in writing: did inheritance remove the if, or just move it? If it merely moved it, would composition have been the better tool?


12. Testing checklist

  • [ ] Opening with a negative balance is refused
  • [ ] Deposit of 0 and of a negative amount are refused
  • [ ] Withdrawal of exactly the balance succeeds and leaves 0
  • [ ] Withdrawal of one pence more is refused and leaves the balance unchanged
  • [ ] A refused transfer leaves both balances identical to before
  • [ ] A successful transfer changes the two balances by exactly the amount
  • [ ] Account numbers are unique and do not repeat after objects are destroyed
  • [ ] The printed statement shows minor units with the correct decimal point only at display

13. Documentation checklist

  • [ ] The invariant written at the top of the header
  • [ ] Every public member function documented with what it refuses and what it guarantees
  • [ ] The meaning of copying an account written down, with the reason
  • [ ] A worked example of a refused transfer in the README or a block comment
  • [ ] A note on where money is converted to and from display form

14. Self-evaluation rubric

Criterion Excellent looks like Weight
Invariant Impossible to reach a negative balance from outside, demonstrated by trying 30%
Failure behaviour Every refusal is complete, tested, and leaves state untouched 25%
Interface Small, honest, and impossible to misuse accidentally 20%
Money handling Integer minor units everywhere; conversion only at display 10%
Static members Unique numbers, with the lifetime question answered 5%
Documentation Invariant and refusal rules written for a reader who did not write the code 10%

Related: Mini projects · Project roadmap · All projects · Module 4 — Encapsulation