Kelick Tech Hub

Build, learn & keep up — tech, Linux & sports news included.

C++ Bank Management System Project

Intermediate•2–3 days•OOP, inheritance, file handling

Multiple account types, deposits, transfers, and interest — a mini banking core.

Share on X

What you will build

Grow the ATM idea into a small bank: many customers, savings and current accounts with different rules, transfers between accounts, and interest calculation. It's the classic OOP workout — inheritance and polymorphism doing real work, not decoration.

What you will learn

Feature checklist

Starter code

#include <iostream>
#include <string>
#include <memory>
#include <vector>

class Account {
protected:
    std::string owner_; double balance_ = 0;
public:
    Account(std::string o) : owner_(std::move(o)) {}
    virtual ~Account() = default;
    virtual bool withdraw(double amt) = 0;         // each type: own rules
    void deposit(double amt) { if (amt > 0) balance_ += amt; }
    double balance() const { return balance_; }
    const std::string& owner() const { return owner_; }
};

class Savings : public Account {
public:
    using Account::Account;
    bool withdraw(double amt) override {
        if (amt <= 0 || amt > balance_) return false;
        balance_ -= amt; return true;
    }
    void applyInterest() { balance_ *= 1.04; }     // 4% yearly
};

class Current : public Account {
    double overdraft_ = 200.0;
public:
    using Account::Account;
    bool withdraw(double amt) override {
        if (amt <= 0 || amt > balance_ + overdraft_) return false;
        balance_ -= amt; return true;
    }
};

// Transfer: withdraw from one, deposit to the other;
// if the withdraw fails, nothing changes.

Full walkthrough

Step 1: Design the Account base class

class Account holds what every account shares — number, holder name, balance — and the operations deposit() and withdraw(). The balance stays protected/private; subclasses and menus touch it only through methods, the ATM project's lesson scaled up.

Step 2: Derive Savings and Current with different rules

class Savings : public Account adds applyInterest() (say 5% a year, applied on demand). class Current : public Account overrides withdraw() to allow an overdraft down to -5000 and charges no interest. Same interface, different rules — that is polymorphism doing real work, not decoration.

class Savings : public Account {
public:
    void applyInterest() { balance += balance * 0.05; }
};
class Current : public Account {
public:
    bool withdraw(double amt) override;  // allows overdraft to -5000
};

Step 3: Store accounts polymorphically

Keep std::vector<std::unique_ptr<Account>> — unique_ptr from <memory> because a vector of Account VALUES would slice off the derived parts (a Savings stored by value becomes a plain Account and loses applyInterest). The base class needs a virtual destructor and virtual withdraw() for any of this to behave.

Step 4: Implement transfer as withdraw + deposit

A transfer finds both accounts by number, calls withdraw() on the source (its own rules apply — a Current account can transfer into its overdraft, a Savings cannot go negative), and only if that succeeds, deposit() into the destination. If the deposit cannot fail, the pair is effectively atomic; never deposit first, or a failed withdrawal creates money.

Step 5: Record a transaction history per account

Every successful deposit/withdraw/transfer appends a string (or small struct) to that account's history vector: 'DEPOSIT +10000 -> balance 60000'. Histories turn 'my money is wrong' from an argument into a lookup — print them from the account menu.

Step 6: Persist accounts with a type tag

Save one line per account starting with S or C so load() knows which class to construct — the factory decision is the interesting part: read the tag, make_unique<Savings> or make_unique<Current>, then fill the shared fields. Histories can be saved too, or rebuilt empty; decide and document it.

Step 7: Test the rules, not the happy path

Deposit and withdraw on both types; try to overdraw a Savings (refused) and a Current past -5000 (refused at the floor); apply interest to 100000 and expect exactly 105000; transfer between accounts and confirm the total money in the system is unchanged. Conservation of money is your test oracle.

Common mistakes

Compile and run

g++ -std=c++17 -o bank bank.cpp

Sample run

$ ./bank
1) Open  2) Deposit  3) Withdraw  4) Transfer  5) Interest  6) History  0) Exit
> 1
Type (S)avings or (C)urrent:
> S
Holder name:
> Ada Lovelace
Account opened: #1001 (Savings)
1) Open  2) Deposit  3) Withdraw  4) Transfer  5) Interest  6) History  0) Exit
> 2
Account # and amount:
> 1001, 100000
Deposited. Balance: 100000.00
> 5
Interest on #1001 at 5%. New balance: 105000.00
> 3
Account # and amount:
> 1001, 200000
Refused: savings accounts cannot be overdrawn.
> 6
History #1001: DEPOSIT +100000.00 -> 100000.00 · INTEREST +5000.00 -> 105000.00
> 0

Try it in your browser

No browser demo is included for this project. Build the C++ version below — that is the real program.

Related Intermediate projects

← All 60 projects · Classic site (Learn & Sandbox)

Download Fake GPS