課題1:コンポジションによるGUIコンポーネントの設計
ボタンとウィンドウの関係をコンポジション(has-a関係)で表現します。WindowクラスがWidgetクラスを含む形で実装します。
Widget.hpp
#pragma once
#include <iostream>
#include <string>
class Widget {
public:
explicit Widget(const std::string &name);
const std::string& name() const;
void activate();
private:
std::string widget_name;
};
Widget::Widget(const std::string &name) : widget_name{name} {}
inline const std::string& Widget::name() const {
return widget_name;
}
inline void Widget::activate() {
std::cout << "Widget [" << widget_name << "] activated\n";
}
Frame.hpp
#pragma once
#include <iostream>
#include <vector>
#include <algorithm>
#include "widget.hpp"
class Frame {
public:
explicit Frame(const std::string &caption);
void render() const;
void shutdown();
void attach(const std::string &widget_name);
void trigger(const std::string &widget_name);
private:
bool exists(const std::string &name) const;
std::string caption;
std::vector<Widget> widgets;
};
Frame::Frame(const std::string &caption_) : caption{caption_} {
widgets.emplace_back("exit");
}
inline void Frame::render() const {
std::string border(50, '=');
std::cout << border << '\n';
std::cout << "Frame: " << caption << '\n';
int num = 0;
for (const auto &w : widgets) {
std::cout << ++num << ". " << w.name() << '\n';
}
std::cout << border << '\n';
}
inline void Frame::shutdown() {
std::cout << "Shutting down frame '" << caption << "'\n";
trigger("exit");
}
inline bool Frame::exists(const std::string &name) const {
return std::any_of(widgets.begin(), widgets.end(),
[&name](const Widget &w) { return w.name() == name; });
}
inline void Frame::attach(const std::string &widget_name) {
if (exists(widget_name)) {
std::cout << "Widget '" << widget_name << "' already exists!\n";
} else {
widgets.emplace_back(widget_name);
}
}
inline void Frame::trigger(const std::string &widget_name) {
for (auto &w : widgets) {
if (w.name() == widget_name) {
w.activate();
return;
}
}
std::cout << "Widget not found: " << widget_name << '\n';
}
main.cpp
#include "frame.hpp"
void demo() {
Frame frm("Application");
frm.attach("create");
frm.attach("delete");
frm.attach("update");
frm.attach("create"); // 重複追加テスト
frm.render();
frm.shutdown();
}
int main() {
std::cout << "=== Composition Pattern Demo ===\n";
demo();
return 0;
}
課題2:標準コンテナの深いコピー動作の検証
std::vectorのコピー構築と要素の独立性を確認します。ネストしたベクタの動作も検証します。
#include <iostream>
#include <vector>
void verify_flat_vector();
void verify_nested_vector();
void show_elements(const std::vector<int> &vec);
void show_matrix(const std::vector<std::vector<int>> &mat);
int main() {
std::cout << "=== 深いコピー検証:1次元vector ===\n";
verify_flat_vector();
std::cout << "\n=== 深いコピー検証:2次元vector ===\n";
verify_nested_vector();
}
void verify_flat_vector() {
std::vector<int> original(5, 100);
const std::vector<int> duplicate(original);
std::cout << "--- コピー直後 ---\n";
std::cout << "original: "; show_elements(original);
std::cout << "duplicate: "; show_elements(duplicate);
original.front() = -999;
std::cout << "--- original[0]変更後 ---\n";
std::cout << "original: "; show_elements(original);
std::cout << "duplicate: "; show_elements(duplicate);
}
void verify_nested_vector() {
std::vector<std::vector<int>> original{{10, 20, 30}, {40, 50, 60, 70}};
const std::vector<std::vector<int>> duplicate(original);
std::cout << "--- コピー直後 ---\n";
std::cout << "original:\n"; show_matrix(original);
std::cout << "duplicate:\n"; show_matrix(duplicate);
original.at(0).push_back(-1);
std::cout << "--- original[0]に要素追加後 ---\n";
std::cout << "original:\n"; show_matrix(original);
std::cout << "duplicate:\n"; show_matrix(duplicate);
}
void show_elements(const std::vector<int> &vec) {
if (vec.empty()) {
std::cout << "(empty)\n";
return;
}
auto it = vec.begin();
std::cout << *it;
for (++it; it != vec.end(); ++it) {
std::cout << ", " << *it;
}
std::cout << '\n';
}
void show_matrix(const std::vector<std::vector<int>> &mat) {
for (const auto &row : mat) {
show_elements(row);
}
}
課題3:カスタム動的配列クラスの実装
RAII原則に基づき、動的メモリ管理を完全にカプセル化したIntArrayクラスを作成します。
#pragma once
#include <iostream>
#include <cstdlib>
class IntArray {
public:
IntArray();
explicit IntArray(int length);
IntArray(int length, int init_val);
IntArray(const IntArray &other);
~IntArray();
int length() const;
int& access(int idx);
const int& access(int idx) const;
IntArray& copy_from(const IntArray &source);
int* data();
int* data_end();
const int* data() const;
const int* data_end() const;
private:
int size;
int *buffer;
};
IntArray::IntArray() : size{0}, buffer{nullptr} {}
IntArray::IntArray(int length) : size{length}, buffer{new int[length]} {}
IntArray::IntArray(int length, int init_val) : size{length}, buffer{new int[length]} {
for (int i = 0; i < size; ++i) {
buffer[i] = init_val;
}
}
IntArray::IntArray(const IntArray &other) : size{other.size}, buffer{new int[other.size]} {
for (int i = 0; i < size; ++i) {
buffer[i] = other.buffer[i];
}
}
IntArray::~IntArray() {
delete[] buffer;
}
int IntArray::length() const {
return size;
}
const int& IntArray::access(int idx) const {
if (idx < 0 || idx >= size) {
std::cerr << "IndexError: position " << idx << " out of bounds\n";
std::exit(EXIT_FAILURE);
}
return buffer[idx];
}
int& IntArray::access(int idx) {
if (idx < 0 || idx >= size) {
std::cerr << "IndexError: position " << idx << " out of bounds\n";
std::exit(EXIT_FAILURE);
}
return buffer[idx];
}
IntArray& IntArray::copy_from(const IntArray &source) {
if (this == &source) return *this;
int *temp = new int[source.size];
for (int i = 0; i < source.size; ++i) {
temp[i] = source.buffer[i];
}
delete[] buffer;
size = source.size;
buffer = temp;
return *this;
}
int* IntArray::data() { return buffer; }
int* IntArray::data_end() { return buffer + size; }
const int* IntArray::data() const { return buffer; }
const int* IntArray::data_end() const { return buffer + size; }
課題4:動的2次元配列(行列)クラスの実装
デリゲートコンストラクタとconst_castによるコード重複排除を活用した行列クラスです。
Matrix.hpp
#pragma once
#include <iostream>
#include <cstdlib>
class Matrix {
public:
Matrix(int rows, int cols, double init = 0.0);
explicit Matrix(int dim, double init = 0.0);
Matrix(const Matrix &other);
~Matrix();
void load(const double *src, int count);
void zero_fill();
double& elem(int row, int col);
const double& elem(int row, int col) const;
int row_count() const;
int col_count() const;
void display() const;
private:
int rows;
int cols;
double *storage;
};
Matrix.cpp
#include "matrix.hpp"
Matrix::Matrix(int row_cnt, int col_cnt, double init)
: rows{row_cnt}, cols{col_cnt}, storage{new double[row_cnt * col_cnt]} {
if (row_cnt <= 0 || col_cnt <= 0) {
std::cerr << "Error: Invalid dimensions\n";
std::exit(EXIT_FAILURE);
}
for (int i = 0; i < rows * cols; ++i) {
storage[i] = init;
}
}
Matrix::Matrix(int dim, double init) : Matrix(dim, dim, init) {}
Matrix::Matrix(const Matrix &other)
: rows{other.rows}, cols{other.cols}, storage{new double[other.rows * other.cols]} {
for (int i = 0; i < rows * cols; ++i) {
storage[i] = other.storage[i];
}
}
Matrix::~Matrix() {
delete[] storage;
}
void Matrix::load(const double *src, int count) {
if (src == nullptr) {
std::cerr << "Error: Null source pointer\n";
std::exit(EXIT_FAILURE);
}
if (count != rows * cols) {
std::cerr << "Error: Size mismatch\n";
std::exit(EXIT_FAILURE);
}
for (int i = 0; i < count; ++i) {
storage[i] = src[i];
}
}
void Matrix::zero_fill() {
for (int i = 0; i < rows * cols; ++i) {
storage[i] = 0.0;
}
}
const double& Matrix::elem(int row, int col) const {
if (row < 0 || row >= rows || col < 0 || col >= cols) {
std::cerr << "IndexError: (" << row << "," << col << ") out of range\n";
std::exit(EXIT_FAILURE);
}
return storage[row * cols + col];
}
double& Matrix::elem(int row, int col) {
return const_cast<double&>(
static_cast<const Matrix*>(this)->elem(row, col)
);
}
int Matrix::row_count() const { return rows; }
int Matrix::col_count() const { return cols; }
void Matrix::display() const {
for (int i = 0; i < rows; ++i) {
for (int j = 0; j < cols; ++j) {
if (j > 0) std::cout << ", ";
std::cout << storage[i * cols + j];
}
std::cout << '\n';
}
}
課題5:連絡先管理システム
ソートと検索機能を持つ連絡先管理クラスを実装します。
Person.hpp
#pragma once
#include <iostream>
#include <string>
class Person {
public:
Person(const std::string &nm, const std::string &tel);
const std::string& name() const;
const std::string& phone() const;
void print() const;
private:
std::string person_name;
std::string person_phone;
};
Person::Person(const std::string &nm, const std::string &tel)
: person_name{nm}, person_phone{tel} {}
const std::string& Person::name() const { return person_name; }
const std::string& Person::phone() const { return person_phone; }
void Person::print() const {
std::cout << person_name << " | " << person_phone;
}
AddressBook.hpp
#pragma once
#include <vector>
#include <algorithm>
#include "person.hpp"
class AddressBook {
public:
void register_contact(const std::string &name, const std::string &phone);
void unregister(const std::string &name);
void search(const std::string &name) const;
void list_all() const;
size_t count() const;
private:
int locate(const std::string &name) const;
void reorder();
std::vector<Person> entries;
};
void AddressBook::register_contact(const std::string &name, const std::string &phone) {
if (locate(name) < 0) {
entries.emplace_back(name, phone);
std::cout << "Added: " << name << '\n';
reorder();
} else {
std::cout << "Exists: " << name << " (add failed)\n";
}
}
void AddressBook::unregister(const std::string &name) {
int pos = locate(name);
if (pos >= 0) {
entries.erase(entries.begin() + pos);
std::cout << "Removed: " << name << '\n';
} else {
std::cout << "Not found: " << name << '\n';
}
}
void AddressBook::search(const std::string &name) const {
int pos = locate(name);
if (pos >= 0) {
entries[pos].print();
std::cout << '\n';
} else {
std::cout << "No match for: " << name << '\n';
}
}
void AddressBook::list_all() const {
for (const auto &e : entries) {
e.print();
std::cout << '\n';
}
}
size_t AddressBook::count() const { return entries.size(); }
int AddressBook::locate(const std::string &name) const {
for (size_t i = 0; i < entries.size(); ++i) {
if (entries[i].name() == name) {
return static_cast<int>(i);
}
}
return -1;
}
void AddressBook::reorder() {
std::sort(entries.begin(), entries.end(),
[](const Person &a, const Person &b) {
return a.name() < b.name();
});
}
main.cpp
#include "addressbook.hpp"
void run_test() {
AddressBook book;
std::cout << "=== Registration ===\n";
book.register_contact("Yamada", "090-1234-5678");
book.register_contact("Suzuki", "080-8765-4321");
book.register_contact("Tanaka", "070-1111-2222");
book.register_contact("Yamada", "090-9999-8888"); // 重複テスト
std::cout << "\n=== Directory (" << book.count() << " entries) ===\n";
book.list_all();
std::cout << "\n=== Search ===\n";
book.search("Suzuki");
book.search("NonExistent");
std::cout << "\n=== Removal ===\n";
book.unregister("Yamada");
book.unregister("Unknown");
}
int main() {
run_test();
return 0;
}