GUI Component Simulation with Composition
Object composition allows building complex systems from simpler components. A graphical user interface framework demonstrates this principle effectively, where a Window container manages multiple Button elements through a standard library container.
Button Component Implementation
#pragma once
#include <iostream>
#include <string>
using std::string;
using std::cout;
class Button {
public:
explicit Button(const string& text);
string get_label() const;
void click();
private:
string label;
};
Button::Button(const string& text) : label{text} {}
inline string Button::get_label() const {
return label;
}
void Button::click() {
cout << "Button '" << label << "' was clicked\n";
}Window Container Class
#pragma once
#include "button.hpp"
#include <vector>
#include <iostream>
using std::vector;
using std::cout;
using std::endl;
class Window {
public:
explicit Window(const string& win_title);
void display() const;
void close();
void add_button(const string& label);
private:
string title;
vector<Button> buttons;
};
Window::Window(const string& win_title) : title{win_title} {
buttons.push_back(Button("close"));
}
inline void Window::display() const {
string separator(40, '*');
cout << separator << endl;
cout << "Window title: " << title << endl;
cout << "Contains " << buttons.size() << " buttons:" << endl;
for (const auto& btn : buttons) {
cout << btn.get_label() << " button" << endl;
}
cout << separator << endl;
}
void Window::close() {
cout << "Closing window '" << title << "'" << endl;
buttons.at(0).click();
}
void Window::add_button(const string& label) {
buttons.push_back(Button(label));
}Application Entry Point
#include "window.hpp"
#include <iostream>
using std::cout;
using std::cin;
void test() {
Window w1("new window");
w1.add_button("maximize");
w1.display();
w1.close();
}
int main() {
cout << "Simulating simple GUI with composition:\n";
test();
}The Window class maintains a vector<Button> member, establishing a composition relationship where the window owns and manages its button components. The close() and display() methods are marked const since they do not modify member state.
Understanding Vector Copy Semantics
The standard library vector container implements deep copy semantics through its copy constructor and assignment operator.
#include <iostream>
#include <vector>
using namespace std;
void printVector(const vector<int>& v) {
for (auto& elem : v)
cout << elem << ", ";
cout << "\b\b \n";
}
void printMatrix(const vector<vector<int>>& m) {
for (auto& row : m) {
for (auto& col : row)
cout << col << ", ";
cout << "\b\b \n";
}
}
void testCopySemantics() {
vector<int> v1(5, 42);
const vector<int> v2(v1);
v1.at(0) = -999;
cout << "v1: "; printVector(v1);
cout << "v2: "; printVector(v2);
}
void testNestedVectors() {
vector<vector<int>> v1{{1, 2, 3}, {4, 5, 6, 7}};
const vector<vector<int>> v2(v1);
v1.at(0).push_back(-999);
cout << "v1:\n"; printMatrix(v1);
cout << "v2:\n"; printMatrix(v2);
vector<int> temp1 = v1.at(0);
cout << "Last element of temp1: " << temp1.at(temp1.size() - 1) << endl;
const vector<int> temp2 = v2.at(0);
cout << "Last element of temp2: " << temp2.at(temp2.size() - 1) << endl;
}
int main() {
cout << "Test 1:\n";
testCopySemantics();
cout << "\nTest 2:\n";
testNestedVectors();
}When v2 is constructed from v1, a complete deep copy occurs. Modifying v1 afterward does not affect v2. This automatic deep copy behavior eliminates the need for manual resource management in most cases.
Custom Dynamic Array Wrapper
Implementing a custom integer array class demonstrates proper resource management, copy semantics, and encapsulation principles.
#pragma once
#include <iostream>
#include <cassert>
using std::cout;
using std::endl;
class DynamicIntArray {
public:
explicit DynamicIntArray(int n);
DynamicIntArray(int n, int value);
DynamicIntArray(const DynamicIntArray& other);
~DynamicIntArray();
int& at(int index);
const int& at(int index) const;
DynamicIntArray& assign(const DynamicIntArray& other);
int get_size() const;
private:
int size;
int* data;
};
DynamicIntArray::DynamicIntArray(int n) : size{n}, data{new int[size]} {}
DynamicIntArray::DynamicIntArray(int n, int value) : size{n}, data{new int[size]} {
for (auto i = 0; i < size; ++i)
data[i] = value;
}
DynamicIntArray::DynamicIntArray(const DynamicIntArray& other)
: size{other.size}, data{new int[size]} {
for (auto i = 0; i < size; ++i)
data[i] = other.data[i];
}
DynamicIntArray::~DynamicIntArray() {
delete[] data;
}
const int& DynamicIntArray::at(int index) const {
assert(index >= 0 && index < size);
return data[index];
}
int& DynamicIntArray::at(int index) {
assert(index >= 0 && index < size);
return data[index];
}
DynamicIntArray& DynamicIntArray::assign(const DynamicIntArray& other) {
delete[] data;
size = other.size;
data = new int[size];
for (int i = 0; i < size; ++i)
data[i] = other.data[i];
return *this;
}
int DynamicIntArray::get_size() const {
return size;
}The copy constructor performs a deep copy by allocating new memory and copying each element. The at() method is overloaded for both const and non-const contexts, enabling read and write access while maintaining const-correctness.
Matrix Class Implementation
A matrix class manages a two-dimensional array stored as a contiguous memory block.
#pragma once
#include <iostream>
#include <cassert>
using std::cout;
using std::endl;
class Matrix {
public:
Matrix(int rows, int cols);
explicit Matrix(int n);
Matrix(const Matrix& other);
~Matrix();
void set(const double* values);
void clear();
const double& at(int row, int col) const;
double& at(int row, int col);
int get_rows() const;
int get_cols() const;
void display() const;
private:
int rows;
int cols;
double* elements;
};
Matrix::Matrix(int r, int c) : rows{r}, cols{c} {
elements = new double[rows * cols];
}
Matrix::Matrix(int n) : rows{n}, cols{n} {
elements = new double[n * n];
}
Matrix::Matrix(const Matrix& other) : rows{other.rows}, cols{other.cols} {
elements = new double[rows * cols];
for (int i = 0; i < rows * cols; ++i)
elements[i] = other.elements[i];
}
Matrix::~Matrix() {
delete[] elements;
}
void Matrix::set(const double* values) {
for (int i = 0; i < rows * cols; ++i)
elements[i] = values[i];
}
void Matrix::clear() {
for (int i = 0; i < rows * cols; ++i)
elements[i] = 0.0;
}
const double& Matrix::at(int r, int c) const {
assert(r >= 0 && r < rows && c >= 0 && c < cols);
return elements[r * cols + c];
}
double& Matrix::at(int r, int c) {
assert(r >= 0 && r < rows && c >= 0 && c < cols);
return elements[r * cols + c];
}
int Matrix::get_rows() const { return rows; }
int Matrix::get_cols() const { return cols; }
void Matrix::display() const {
for (int i = 0; i < rows; ++i) {
for (int j = 0; j < cols; ++j)
cout << at(i, j) << " ";
cout << endl;
}
}User Authentication System
A user management class encapsulates credentials and provides password modification capabilities.
#pragma once
#include <string>
#include <iostream>
using std::string;
using std::cout;
using std::cin;
class User {
private:
string username;
string password;
string email;
public:
User(const string& name, const string& pwd = "123456", const string& mail = "")
: username{name}, password{pwd}, email{mail} {}
void set_email() {
string input;
bool valid = false;
cout << "Enter email address: ";
while (!valid) {
cin >> input;
for (char c : input) {
if (c == '@') {
valid = true;
break;
}
}
if (valid) {
email = input;
cout << "Email configured successfully.\n";
} else {
cout << "Invalid email format. Re-enter: ";
}
}
}
void change_password() {
int attempts = 3;
string old_pwd, new_pwd;
cout << "Enter current password: ";
while (attempts > 0) {
cin >> old_pwd;
if (old_pwd == password) {
cout << "Enter new password: ";
cin >> new_pwd;
password = new_pwd;
cout << "Password updated successfully.\n";
return;
}
attempts--;
if (attempts > 0)
cout << "Incorrect password. Try again: ";
}
cout << "Too many failed attempts. Try again later.\n";
}
void display() const {
cout << "Username: " << username << endl;
cout << "Password: ";
for (size_t i = 0; i < password.length(); ++i)
cout << "*";
cout << endl;
cout << "Email: " << email << endl;
}
};Banking System with Date Management
Date Class Header
#pragma once
#include <iostream>
class Date {
private:
int year, month, day;
int totalDays;
static const int DAYS_BEFORE_MONTH[];
public:
Date(int y, int m, int d);
int getYear() const { return year; }
int getMonth() const { return month; }
int getDay() const { return day; }
int getMaxDay() const;
bool isLeapYear() const { return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0; }
int distance(const Date& other) const { return totalDays - other.totalDays; }
void show() const;
};Date Implementation
#include "date.h"
#include <cstdlib>
const int Date::DAYS_BEFORE_MONTH[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};
Date::Date(int y, int m, int d) : year{y}, month{m}, day{d} {
if (day <= 0 || day > getMaxDay()) {
std::cout << "Invalid date: ";
show();
std::cout << std::endl;
exit(1);
}
int years = year - 1;
totalDays = years * 365 + years / 4 - years / 100 + years / 400 + DAYS_BEFORE_MONTH[month - 1] + day;
if (isLeapYear() && month > 2) totalDays++;
}
int Date::getMaxDay() const {
if (isLeapYear() && month == 2)
return 29;
return DAYS_BEFORE_MONTH[month] - DAYS_BEFORE_MONTH[month - 1];
}
void Date::show() const {
std::cout << year << "-" << month << "-" << day;
}Savings Account Class
#pragma once
#include "date.h"
#include <string>
class SavingsAccount {
private:
std::string accountId;
double balance;
double rate;
Date lastDate;
double accumulation;
static double total;
void record(const Date& date, double amount, const std::string& desc);
void error(const std::string& msg) const;
double accumulate(const Date& date) const {
return accumulation + balance * date.distance(lastDate);
}
public:
SavingsAccount(const Date& date, const std::string& id, double r);
const std::string& getId() const { return accountId; }
double getBalance() const { return balance; }
double getRate() const { return rate; }
static double getTotal() { return total; }
void deposit(const Date& date, double amount, const std::string& desc);
void withdraw(const Date& date, double amount, const std::string& desc);
void settle(const Date& date);
void show() const;
};Account Implementation
#include "account.h"
#include <cmath>
#include <iostream>
double SavingsAccount::total = 0;
SavingsAccount::SavingsAccount(const Date& date, const std::string& id, double r)
: accountId{id}, balance{0}, rate{r}, lastDate{date}, accumulation{0} {
date.show();
std::cout << "\t#" << id << " created" << std::endl;
}
void SavingsAccount::record(const Date& date, double amount, const std::string& desc) {
accumulation = accumulate(date);
lastDate = date;
amount = floor(amount * 100 + 0.5) / 100;
balance += amount;
total += amount;
date.show();
std::cout << "\t#" << accountId << "\t" << amount << "\t" << balance << "\t" << desc << std::endl;
}
void SavingsAccount::deposit(const Date& date, double amount, const std::string& desc) {
record(date, amount, desc);
}
void SavingsAccount::withdraw(const Date& date, double amount, const std::string& desc) {
if (amount > getBalance())
error("Insufficient funds");
else
record(date, -amount, desc);
}
void SavingsAccount::settle(const Date& date) {
double interest = accumulate(date) * rate / date.distance(Date(date.getYear() - 1, 1, 1));
if (interest != 0)
record(date, interest, "interest");
accumulation = 0;
}
void SavingsAccount::show() const {
std::cout << "Account: " << accountId << "\tBalance: " << balance;
}
void SavingsAccount::error(const std::string& msg) const {
std::cout << "Error (#" << accountId << "): " << msg << std::endl;
}Banking Application
#include "account.h"
#include <iostream>
int main() {
Date startDate(2008, 11, 1);
SavingsAccount accounts[] = {
SavingsAccount(startDate, "03755217", 0.015),
SavingsAccount(startDate, "02342342", 0.015)
};
const int count = sizeof(accounts) / sizeof(SavingsAccount);
accounts[0].deposit(Date(2008, 11, 5), 5000, "salary");
accounts[1].deposit(Date(2008, 11, 25), 10000, "stock sale");
accounts[0].deposit(Date(2008, 12, 5), 5500, "salary");
accounts[1].withdraw(Date(2008, 12, 20), 4000, "laptop purchase");
std::cout << std::endl;
for (int i = 0; i < count; ++i) {
accounts[i].settle(Date(2009, 1, 1));
accounts[i].show();
std::cout << std::endl;
}
std::cout << "Total across all accounts: " << SavingsAccount::getTotal() << std::endl;
return 0;
}The SavingsAccount class maintains a static member total that aggregates balances across all instances, demonstrating class-level state management. Interest calculation uses the accumulation pattern, tracking daily balances to compute periodic interest accurately.