C++ Expense Tracker Project
Log spending by category and see where the money actually goes.
What you will build
Record expenses with amounts and categories, then summarise: totals per category, biggest single expense, spending this month vs last. It's a data-aggregation exercise — grouping, summing, and presenting numbers so they change behaviour.
What you will learn
- Grouping records by category with std::map
- Aggregating sums, averages, and maximums
- Filtering records by date range
- Presenting a readable summary report
Feature checklist
- Add an expense: date, category, amount, note
- Total spending overall and per category
- Monthly summary view
- Largest expense callout
- Data saved to a file between runs
- Delete a mistaken entry
Starter code
#include <iostream>
#include <fstream>
#include <map>
#include <string>
struct Expense { std::string date, category, note; double amount; };
int main() {
std::map<std::string, double> byCategory;
double total = 0, biggest = 0;
std::string biggestWhat;
std::ifstream in("expenses.txt");
Expense e;
while (in >> e.date >> e.category >> e.amount) {
std::getline(in, e.note);
byCategory[e.category] += e.amount;
total += e.amount;
if (e.amount > biggest) { biggest = e.amount; biggestWhat = e.category; }
}
std::cout << "Total spent: $" << total << "\nBy category:\n";
for (const auto& [cat, sum] : byCategory)
std::cout << " " << cat << ": $" << sum
<< " (" << (total ? 100.0 * sum / total : 0) << "%)\n";
std::cout << "Biggest category splurge: " << biggestWhat
<< " $" << biggest << "\n";
}Full walkthrough
Step 1: Model an expense as one struct
struct Expense { std::string date, category, note; double amount; } — one row of spending. All expenses live in a std::vector<Expense>. Dates stay text (YYYY-MM-DD) like the temperature logger, because the monthly view will slice them by prefix.
Step 2: Add entries with validation
Prompt for date, category, amount, note. Refuse amount <= 0 (a negative expense is a refund feature you did not design) and an empty category. Push to the vector and append to expenses.txt immediately — the now-familiar save-on-change rule.
Step 3: Total per category with a map
std::map<std::string, double> byCat; then for each expense, byCat[e.category] += e.amount. operator[] default-creates a 0.0 for a new category, which is exactly right here (contrast with the currency converter, where the same behaviour was a trap — context decides whether [] is a tool or a bug). The map prints categories alphabetically for free.
Step 4: Build the monthly summary by date prefix
A month is date.substr(0, 7). Filter expenses whose prefix matches the requested month, total them overall and per category with the same map trick. Because YYYY-MM-DD sorts lexicographically, string comparison IS chronological comparison — the format earns its keep again.
std::string month = e.date.substr(0, 7);
if (month == wanted) { total += e.amount; byCat[e.category] += e.amount; }Step 5: Call out the largest expense
One pass tracking the maximum-amount Expense (store the whole record, not just the amount, so you can print its date, category, and note). Initialize from the first element and guard the empty list — same lesson as the temperature logger's highest reading.
Step 6: Delete a mistaken entry by number
List expenses with numbers, read the number, erase(begin + n - 1), then rewrite the whole file (append-mode cannot remove lines, so deletion switches the save strategy to full rewrite — a real design consequence worth noticing).
Step 7: Test against hand-added numbers
Enter Food 2500, Transport 1200, Food 800 for one month: overall 4500, Food 3300, Transport 1200, largest 'Food 2500'. Restart and confirm the same report — then delete the 800 entry and confirm Food drops to 2500 and the file agrees after another restart.
Common mistakes
- Using operator[] to LOOK UP a category total without knowing it inserts missing keys — harmless here, catastrophic in the currency converter; learn both faces of it.
- Deleting a line by appending-mode file writes; deletion requires rewriting the file without that line.
- Filtering months by substring search anywhere in the date instead of the 7-char prefix, matching note text that happens to contain '2026-10'.
Compile and run
g++ -std=c++17 -o expenses expenses.cppSample run
$ ./expenses
Loaded 0 expenses.
1) Add 2) Totals 3) Monthly summary 4) Delete 0) Quit
> 1
Date, category, amount, note:
> 2026-10-21, Food, 2500, Market run
Saved.
1) Add 2) Totals 3) Monthly summary 4) Delete 0) Quit
> 1
Date, category, amount, note:
> 2026-10-22, Transport, 1200, Bus fares
Saved.
> 2026-10-22, Food, 800, Snacks
> 3
Month (YYYY-MM):
> 2026-10
Food: 3300.00 · Transport: 1200.00 · TOTAL: 4500.00
Largest expense: Food 2500.00 (Market run, 2026-10-21)
> 0Try it in your browser
No browser demo is included for this project. Build the C++ version below — that is the real program.