Real-Life Example
Complete Banking System Example
This example demonstrates various parameter techniques in a cohesive system:
Example
#include <iostream>
#include <string>
#include <vector>
#include <ctime>
#include <algorithm>
#include <utility>
struct InterestRate {
double value{}; // e.g., 0.02 for 2%
};
struct Transaction {
std::string id;
double amount{};
std::time_t timestamp{};
};
class Account {
std::string id_;
double balance_{0.0};
std::vector<Transaction> history_;
public:
// Constructor: pass-by-value + move to avoid extra copies for id
explicit Account(std::string id, double opening = 0.0)
: id_(std::move(id)), balance_(opening) {}
// Pass by reference for modification
bool transferTo(Account& recipient, double amount) {
if (&recipient == this) return false; // disallow self-transfer
if (amount <= 0) return false; // validate amount
if (balance_ < amount) return false; // insufficient funds
balance_ -= amount;
recipient.balance_ += amount;
// Add transactions to both accounts
addTransaction(-amount);
recipient.addTransaction(amount);
return true;
}
// Pass by const reference for efficiency (read-only large object)
void applyInterest(const InterestRate& rate) {
balance_ *= (1.0 + rate.value);
}
// Default parameter uses a reference to an object with external linkage
void printStatement(std::ostream& out = std::cout) const {
out << "Account: " << id_ << '\n'
<< "Balance: " << balance_ << '\n';
}
// Return by value (RVO/NRVO); most-recent-first
std::vector<Transaction> getRecentTransactions(std::size_t count = 10) const {
auto start = history_.rbegin();
auto slice = std::min(count, history_.size());
auto end = start + static_cast<std::ptrdiff_t>(slice);
return std::vector<Transaction>(start, end); // copies the selected range
}
private:
// Pass by value for small primitive
void addTransaction(double amount) {
history_.push_back(Transaction{ generateId(), amount, std::time(nullptr) });
}
std::string generateId() const {
return "TXN" + std::to_string(history_.size() + 1);
}
};
Key Lessons
1. Reference parameters enable object modification
2. Const references protect against unwanted changes and avoid copies
3. Default parameters provide flexibility (e.g., ostream with std::cout)
4. Return-by-value is efficient with RVO/NRVO—even for containers
5. Validate inputs (e.g., non-positive amounts, self-transfer) and clamp counts when slicing
6. Structuring parameters (value vs reference vs const reference) keeps interfaces clear and intent explicit