C++ Contact Management Project
A digital address book with add, search, edit, delete — and real persistence.
What you will build
A contact manager that stores names, phone numbers, and emails in a file, and lets you search, edit, and delete entries. This is most developers' first CRUD application (create, read, update, delete) — the pattern behind a huge share of all real software.
What you will learn
- Designing a Contact class with private fields
- Serialising objects to a text/binary file
- Case-insensitive substring search
- Sorting collections with std::sort and lambdas
Feature checklist
- Add a contact (name, phone, email)
- List all contacts alphabetically
- Search by partial name
- Edit and delete existing contacts
- Data persists between runs in a file
Starter code
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
struct Contact {
std::string name, phone, email;
};
class ContactBook {
std::vector<Contact> items_;
public:
void add(Contact c) { items_.push_back(std::move(c)); }
std::vector<Contact> find(const std::string& q) const {
std::vector<Contact> hits;
for (const auto& c : items_) {
std::string n = c.name;
for (auto& ch : n) ch = std::tolower(ch);
if (n.find(q) != std::string::npos) hits.push_back(c);
}
return hits;
}
bool remove(const std::string& name) {
auto it = std::remove_if(items_.begin(), items_.end(),
[&](const Contact& c) { return c.name == name; });
bool found = it != items_.end();
items_.erase(it, items_.end());
return found;
}
size_t size() const { return items_.size(); }
};
// main(): menu loop calling add/find/remove,
// then save items_ to a file on exit and load on start.Full walkthrough
Step 1: Model a contact, then a book that owns contacts
Two types: struct Contact { std::string name, phone, email; } is pure data, and class ContactBook owns a std::vector<Contact> plus every operation on it. This split — dumb data, smart owner — is the shape of almost every intermediate project in this catalogue, so learn it here where the stakes are low.
Step 2: Implement add() and keep the book sorted
ContactBook::add pushes the new Contact into the vector. The checklist wants listings alphabetical: either sort after every add with std::sort from <algorithm> (comparing name fields), or insert at the position std::lower_bound finds. Sorting after each add is simpler and plenty fast at address-book scale.
Step 3: Search by partial name
Search loops over the contacts and keeps those whose name CONTAINS the query — name.find(query) != std::string::npos. Normalise case first (lowercase both sides) or 'ada' will miss 'Ada'. Partial search is the feature users actually mean by 'search', and find() gives it to you in one expression.
Step 4: Edit and delete by identity, not by luck
Find the contact's index with the search, show it, then mutate or erase it (erase(begin + i), the same vector surgery as the To-Do list). Deleting by displayed number without re-checking the index after edits is how contact apps eat the wrong person — always operate on the record you just found.
Step 5: Persist to a file with a simple line format
Save one contact per line as name|phone|email ('|' because names and emails never contain it). Load by reading lines and splitting on the delimiter. Save after every mutation, like the To-Do app taught: a contacts app that loses data on a crash is worse than no app.
Step 6: Build the menu around the book
main() owns a ContactBook, loads it, and offers Add / List / Search / Edit / Delete / Quit. Notice how thin main() is now — every branch is a one-line call into the class. If your main() is doing string surgery, that code belongs in ContactBook.
ContactBook book;
book.load("contacts.txt");
// menu: book.add(c); book.listAll();
// book.search("ada"); book.remove(i);Step 7: Test the lifecycle like a user
Add three contacts out of alphabetical order, list (must be sorted), search a partial lowercase fragment, edit a phone number, delete the middle contact, quit, relaunch, and verify everything persisted. The relaunch check is non-negotiable — it is the only test that exercises load().
Common mistakes
- Searching case-sensitively, so 'ada' finds nothing and users conclude search is broken.
- Storing contacts unsorted and sorting only for display, so delete-by-number removes a different record than the one shown.
- Using commas as the file delimiter when names and addresses can contain commas — pick a delimiter your data cannot contain.
Compile and run
g++ -std=c++17 -o contacts contacts.cppSample run
$ ./contacts
Loaded 2 contacts.
1) Add 2) List 3) Search 4) Edit 5) Delete 0) Quit
> 1
Name:
> Chiamaka Eze
Phone:
> 0803 555 0142
Email:
> chiamaka@example.com
Saved.
1) Add 2) List 3) Search 4) Edit 5) Delete 0) Quit
> 2
1. Ada Lovelace — 0805 555 0119 — ada@example.com
2. Bola Ahmed — 0809 555 0173 — bola@example.com
3. Chiamaka Eze — 0803 555 0142 — chiamaka@example.com
> 3
Search name:
> chi
Found: Chiamaka Eze — 0803 555 0142 — chiamaka@example.com
> 0Try it in your browser
No browser demo is included for this project. Build the C++ version below — that is the real program.