C++ OOP and Templates
Build a BankAccount class
Description: Make a class BankAccount with a private balance. Add deposit and withdraw. Throw an exception for a negative deposit and for a withdraw bigger than the balance.
Hint:
- Use
std::invalid_argumentandstd::runtime_error.
Solution
#include <iostream>
#include <stdexcept>
class BankAccount {
public:
void deposit(double amount) {
if (amount <= 0) throw std::invalid_argument("Deposit must be positive");
balance += amount;
}
void withdraw(double amount) {
if (amount > balance) throw std::runtime_error("Not enough balance");
balance -= amount;
}
double get_balance() const { return balance; }
private:
double balance = 0;
};
int main() {
BankAccount acc;
acc.deposit(1000);
acc.withdraw(250);
std::cout << acc.get_balance() << '\n';
try {
acc.withdraw(5000);
} catch (const std::exception& e) {
std::cout << "Error: " << e.what() << '\n';
}
try {
acc.deposit(-5);
} catch (const std::exception& e) {
std::cout << "Error: " << e.what() << '\n';
}
}
750
Error: Not enough balance
Error: Deposit must be positive
How it works
- The balance is private, so only the two methods can change it, and both check the value.
Read Encapsulation.
Calculate areas with polymorphism
Description: Make an abstract class Shape with a pure virtual function area(). Write Circle (radius 2) and Rectangle (3 x 4). Put them in one container and print the total area.
Hint:
- Use
std::vector<std::unique_ptr<Shape>>and a virtual destructor.
Solution
#include <iomanip>
#include <iostream>
#include <memory>
#include <numbers>
#include <vector>
class Shape {
public:
virtual ~Shape() = default;
virtual double area() const = 0;
};
class Circle : public Shape {
public:
explicit Circle(double r) : r(r) {}
double area() const override { return std::numbers::pi * r * r; }
private:
double r;
};
class Rectangle : public Shape {
public:
Rectangle(double w, double h) : w(w), h(h) {}
double area() const override { return w * h; }
private:
double w, h;
};
int main() {
std::vector<std::unique_ptr<Shape>> shapes;
shapes.push_back(std::make_unique<Circle>(2));
shapes.push_back(std::make_unique<Rectangle>(3, 4));
double total = 0;
for (const auto& s : shapes) total += s->area();
std::cout << std::fixed << std::setprecision(2) << "Total area: " << total << '\n';
}
Total area: 24.57
How it works
- The loop uses the base type. C++ calls the
area()of the real object (virtual function). - The virtual destructor makes sure that the right destructor runs when the smart pointers delete the objects.
Read Polymorphism and Abstract Classes.
Overload operators for a Money class
Description: Write a class Money which keeps rupees and paise. Overload +, == and <<. Add Rs. 10.75 and Rs. 5.50, and print Rs. 16.25.
Hint:
- Keep the value in paise (a whole number) to avoid decimal errors.
Solution
#include <iostream>
class Money {
public:
explicit Money(long long paise) : paise(paise) {}
Money operator+(const Money& o) const { return Money(paise + o.paise); }
bool operator==(const Money&) const = default;
friend std::ostream& operator<<(std::ostream& os, const Money& m) {
return os << "Rs. " << m.paise / 100 << '.' << (m.paise % 100 < 10 ? "0" : "") << m.paise % 100;
}
private:
long long paise;
};
int main() {
Money a(1075), b(550);
std::cout << a + b << '\n';
std::cout << std::boolalpha << (a + b == Money(1625)) << '\n';
}
Rs. 16.25
true
How it works
- Money is kept as a whole number of paise, so there is no decimal rounding problem.
operator<<is a friend function, because the stream is on the left side.
Read Operator Overloading.
Write a generic maximum template
Description: Write a function template max_of which returns the bigger of two values. Use it with int, double and std::string. Then limit it to types which can be compared with a concept.
Hint:
std::totally_orderedis a ready concept for comparable types.
Solution
#include <concepts>
#include <iostream>
#include <string>
template <std::totally_ordered T>
T max_of(T a, T b) {
return a > b ? a : b;
}
int main() {
std::cout << max_of(3, 9) << '\n';
std::cout << max_of(2.5, 1.5) << '\n';
std::cout << max_of(std::string("pear"), std::string("apple")) << '\n';
}
9
2.5
pear
How it works
- The compiler makes one version of the function for each type you use.
- The concept gives a clear error when the type can not be compared.
Read C++ Templates.