C++ Library Management System Project
Track books, members, loans, and overdue fines like a real small library.
What you will build
Catalogue books, register members, issue and return books, and fine anyone who keeps one too long. The project's heart is relationships between entities — a book on loan points at a member — which is the core skill of business software.
What you will learn
- Modelling relationships between classes
- Using std::map for ID-based lookup
- Date handling for due dates and fines
- Loading/saving a multi-entity dataset
Feature checklist
- Add books (ISBN, title, author, copies)
- Register members
- Issue a book: records member + due date
- Return a book: computes overdue fine
- Search the catalogue by title or author
- List all books currently on loan
Starter code
#include <iostream>
#include <map>
#include <string>
struct Book { std::string title, author; int copies = 0; };
struct Loan { std::string isbn, memberId; int dueDay; }; // days since epoch
class Library {
std::map<std::string, Book> books_;
std::map<std::string, Loan> loans_; // key: isbn + member
public:
void addBook(std::string isbn, Book b) { books_[isbn] = std::move(b); }
bool issue(const std::string& isbn, const std::string& member,
int today) {
auto it = books_.find(isbn);
if (it == books_.end() || it->second.copies == 0) return false;
--it->second.copies;
loans_[isbn + "|" + member] = {isbn, member, today + 14};
return true;
}
double returnBook(const std::string& isbn, const std::string& member,
int today) {
auto key = isbn + "|" + member;
auto it = loans_.find(key);
if (it == loans_.end()) return -1;
int late = today - it->second.dueDay;
loans_.erase(it);
++books_[isbn].copies;
return late > 0 ? late * 0.50 : 0.0; // $0.50 per late day
}
};Full walkthrough
Step 1: Model Book and Loan as separate facts
struct Book { isbn, title, author, copies } describes the catalogue; struct Loan { isbn, memberId, dueDay } describes an event — someone took a book. Keeping them separate matters because a book exists once while its loans come and go. class Library owns both collections: a std::map<std::string, Book> keyed by ISBN and a vector of active Loans.
Step 2: addBook() and member registration
addBook inserts into the map (or tops up copies if the ISBN already exists — same book, more copies). Members live in a map from member ID to name. Registration is boring code; the interesting decision is the KEY: ISBN for books because titles repeat and spellings vary, member ID for people for the same reason.
Step 3: issue(): the three checks before a loan exists
Look up the book (unknown ISBN → refuse), count current loans of that ISBN (loans == copies → 'all copies out'), confirm the member exists. Only then push a Loan with a due date 14 days out. The count-the-loans step is where beginners stumble: availability is DERIVED from loans, never stored as a separate counter that can drift.
bool Library::issue(isbn, member) {
if (!books.count(isbn)) return false;
if (activeLoansOf(isbn) >= books[isbn].copies) return false;
loans.push_back({isbn, member, today() + 14});
return true;
}Step 4: returnBook(): find the loan, compute the fine
Find the matching active Loan, erase it, and compare today with its dueDay: fine = overdueDays × 2.50 (state your rate in a named constant). Returning a book that was never issued must be an error message, not a crash — the find can come back empty and your code must expect that.
Step 5: Search the catalogue by text
Loop the map and match title or author with a case-insensitive substring search, printing each hit with its availability computed on the spot ('3 of 5 copies available'). Same partial-search skill as the contact book, now with derived status attached.
Step 6: List everything on loan
Walk the loans vector and join each against the books map to print title + member + due date, flagging overdue ones. This 'join two collections by key' move is the database instinct arriving early — the map lookup inside the loop is doing exactly what a SQL JOIN does.
Step 7: Persist and test the full cycle
Save books, members, and loans to files on every change and reload at startup. Test: add a book with 1 copy, issue it, issue it again (must refuse), return it 3 days late (fine 7.50 at 2.50/day), issue it again (must succeed now).
Common mistakes
- Storing an 'availableCopies' counter separately from the loans list — the two drift apart after the first bug, and derived-from-loans is always truthful.
- Letting returnBook() erase without finding a matching loan first, crashing or fining the wrong member.
- Checking availability as copies > 0 instead of copies minus active loans.
Compile and run
g++ -std=c++17 -o library library.cppSample run
$ ./library
1) Add book 2) Register member 3) Issue 4) Return 5) Search 6) On loan 0) Exit
> 1
ISBN:
> 978-0132350884
Title — Author:
> Clean Code — Robert C. Martin
Copies:
> 2
Added.
1) Add book 2) Register member 3) Issue 4) Return 5) Search 6) On loan 0) Exit
> 3
ISBN and member ID (M101 = Ada Lovelace, registered in step 2):
> 978-0132350884, M101
Issued. Due back in 14 days (6 Nov 2026).
> 5
Search:
> clean
Clean Code by Robert C. Martin — 1 of 2 copies available
> 6
ON LOAN: Clean Code -> M101 (Ada Lovelace), due 6 Nov 2026
> 0Try it in your browser
No browser demo is included for this project. Build the C++ version below — that is the real program.