C++ Simple Voting System Project
Register voters, cast one ballot each, and tally a tamper-resistant result.
What you will build
An election in miniature: candidates are registered, voters authenticate, each voter gets exactly one ballot, and results tally at the end. The interesting problems are integrity ones — preventing double voting and tying every ballot to a verified voter.
What you will learn
- Using std::map to tally counts by key
- One-person-one-vote enforcement with a set
- Separating voter registry from ballot data
- Presenting results with percentages and a winner
Feature checklist
- Admin adds candidates
- Voters register with a unique ID
- Each registered voter can vote exactly once
- Live tally with vote counts and percentages
- Winner announcement, including tie handling
- Results saved to a file
Starter code
#include <iostream>
#include <map>
#include <set>
#include <string>
class Election {
std::map<std::string, int> tally_; // candidate -> votes
std::set<std::string> hasVoted_;
public:
void addCandidate(const std::string& name) { tally_[name] = 0; }
bool castVote(const std::string& voterId, const std::string& cand) {
if (!tally_.count(cand)) return false; // unknown candidate
if (hasVoted_.count(voterId)) return false; // already voted
hasVoted_.insert(voterId);
++tally_[cand];
return true;
}
void results() const {
int total = 0;
for (auto& [name, n] : tally_) total += n;
std::string winner; int best = -1;
for (auto& [name, n] : tally_) {
double pct = total ? 100.0 * n / total : 0;
std::cout << name << ": " << n << " votes (" << pct << "%)\n";
if (n > best) { best = n; winner = name; }
}
std::cout << "Winner: " << winner << "\n";
}
};Full walkthrough
Step 1: Model the election in one class
class Election owns everything: a std::map<std::string, int> tally (candidate name → votes) and a std::set<std::string> registered voter IDs plus a set of who has already voted. One class holding candidates AND voters is right at this scale — and the sets are the integrity mechanism, as you will see.
Step 2: addCandidate() with duplicate protection
Admin mode adds candidates into the tally map with 0 votes. Adding the same name twice must not reset an existing count — check count() first, or use emplace which refuses to overwrite. A candidate added mid-election silently zeroing their votes is the kind of bug that ends democracies and demo days.
Step 3: Register voters with unique IDs
Registration inserts the voter's ID into the registered set; insert() returns a pair whose .second tells you whether it was actually new. Duplicate registration is refused with a message. The set does the uniqueness work — no manual scanning.
Step 4: castVote(): the three-gate function
Gate 1: is this ID registered? Gate 2: has this ID already voted (membership in the voted set)? Gate 3: does the chosen candidate exist? Only then tally[candidate]++ and insert the ID into voted. The ORDER of the gates is the audit story: each refusal message names the exact rule that fired.
bool Election::castVote(id, candidate) {
if (!registered.count(id)) return refuse("not registered");
if (voted.count(id)) return refuse("already voted");
if (!tally.count(candidate)) return refuse("no such candidate");
tally[candidate]++;
voted.insert(id);
return true;
}Step 5: Report the tally with percentages
Total the votes, then print each candidate with count and 100.0 * votes / total percent (guard total == 0 before any voting). Using a double literal 100.0 avoids the integer-division trap from the quiz project. Sort the printout by votes descending — copy the map into a vector of pairs and sort it — so the leader is always on top.
Step 6: Announce the winner, including ties
Find the maximum vote count, collect ALL candidates holding it, and if there is more than one, announce a tie by name. 'First max wins' code that ignores ties will happily crown one of two equal candidates; collecting all maxima is three extra lines and the only honest version.
Step 7: Persist results and registrations
Save candidates, tallies, and both ID sets to a file so an election survives a restart — a results file the checklist demands, and the voted set must be saved too, or a restart re-opens voting to everyone (the digital equivalent of washing off the ink mark).
Common mistakes
- Checking 'already voted' AFTER incrementing the tally, so a rejected second vote still counted.
- Announcing only the first candidate with the max votes and never detecting a tie.
- Saving the tally but not the voted set, so restarting the program lets everyone vote again.
Compile and run
g++ -std=c++17 -o voting voting.cppSample run
$ ./voting
Mode: (A)dmin or (V)oter or (R)esults?
> A
Add candidate:
> Ada
Add candidate:
> Bola
Mode: (A)dmin or (V)oter or (R)esults?
> V
Your voter ID:
> V001
Candidates: 1) Ada 2) Bola — your choice:
> 1
Vote recorded for Ada. Thank you!
> V
Your voter ID:
> V001
Refused: voter V001 has already voted.
> R
Ada: 3 votes (60.0%)
Bola: 2 votes (40.0%)
Winner: AdaTry it in your browser
No browser demo is included for this project. Build the C++ version below — that is the real program.