C++ オブジェクト指向設計の実践:リソース管理とインターフェース設計の詳細

以下はC++を用いたオブジェクト指向設計とリソース管理の実装例についての詳細です。各タスクでは、クラス設計、メモリ管理、カプセル化の実践を通じて、効率的で安全なコード作成の手法を学びます。

タスク1:GUIコンポーネントの実装


#pragma once
#include <iostream>
#include <string>

class PushButton {
public:
    PushButton(const std::string& text);
    const std::string& getText() const;
    void activate();
private:
    std::string text_;
};

#include "window_manager.hpp"
#include <iostream>

void demo() {
    WindowManager w("サンプル");
    w.addControl("追加");
    w.addControl("削除");
    w.addControl("変更");
    w.addControl("追加");
    w.show();
    w.close();
}

int main() {
    std::cout << "コンポジションによるGUIシミュレーション:\n";
    demo();
}

#pragma once
#include <iostream>
#include <vector>
#include <algorithm>
#include "pushbutton.hpp"

class WindowManager {
public:
    WindowManager(const std::string& title);
    void show() const;
    void close();
    void addControl(const std::string& label);
    void triggerControl(const std::string& label);
private:
    bool hasControl(const std::string& label) const;
private:
    std::string title_;
    std::vector<PushButton> controls;
};

タスク2:深コピーの検証


#include <iostream>
#include <vector>

void verifyCopy1();
void verifyCopy2();
void printVector(const std::vector<int>& v);
void printNestedVector(const std::vector<std::vector<int>>& v);

int main() {
    std::cout << "深コピー検証1: std::vector<int>\n";
    verifyCopy1();
    std::cout << "\n深コピー検証2: ネストされたstd::vector<int>\n";
    verifyCopy2();
}

void verifyCopy1() {
    std::vector<int> v1(5, 42);
    const std::vector<int> v2(v1);
    std::cout << "--- コピー後 ---\n";
    std::cout << "v1: "; printVector(v1);
    std::cout << "v2: "; printVector(v2);
    v1[0] = -1;
    std::cout << "--- v1[0]変更後 ---\n";
    std::cout << "v1: "; printVector(v1);
    std::cout << "v2: "; printVector(v2);
}

void verifyCopy2() {
    std::vector<std::vector<int>> v1{{1, 2, 3}, {4, 5, 6, 7}};
    const std::vector<std::vector<int>> v2(v1);
    std::cout << "--- コピー後 ---\n";
    std::cout << "v1: "; printNestedVector(v1);
    std::cout << "v2: "; printNestedVector(v2);
    v1[0].push_back(-1);
    std::cout << "--- v1[0]変更後 ---\n";
    std::cout << "v1: "; printNestedVector(v1);
    std::cout << "v2: "; printNestedVector(v2);
}

void printVector(const std::vector<int>& v) {
    if (v.empty()) {
        std::cout << '\n';
        return;
    }
    std::cout << v[0];
    for (size_t i = 1; i < v.size(); ++i)
        std::cout << ", " << v[i];
    std::cout << '\n';
}

void printNestedVector(const std::vector<std::vector<int>>& v) {
    for (const auto& row : v) {
        printVector(row);
    }
}

タスク3:独自のベクターコンテナの実装


#pragma once
#include <iostream>

class IntVector {
public:
    IntVector();
    IntVector(int size);
    IntVector(int size, int value);
    IntVector(const IntVector& other);
    ~IntVector();
    int getSize() const;
    int& getAt(int index);
    const int& getAt(int index) const;
    IntVector& assign(const IntVector& other);
    int* begin();
    int* end();
    const int* begin() const;
    const int* end() const;
private:
    int size_;
    int* data_;
};

#include "intvector.hpp"
#include <iostream>

void test1();
void test2();
void display1(const IntVector& vec);
void display2(const IntVector& vec);

int main() {
    std::cout << "テスト1:\n";
    test1();
    std::cout << "\nテスト2:\n";
    test2();
}

void test1() {
    int n;
    std::cout << "サイズを入力: ";
    std::cin >> n;
    IntVector x1(n);
    for (int i = 0; i < n; ++i)
        x1.getAt(i) = (i + 1) * 10;
    std::cout << "x1: "; display1(x1);
    IntVector x2(n, 42);
    IntVector x3(x2);
    x2.getAt(0) = -1;
    std::cout << "x2: "; display1(x2);
    std::cout << "x3: "; display1(x3);
}

void test2() {
    const IntVector x(5, 42);
    IntVector y;
    y.assign(x);
    std::cout << "x: "; display2(x);
    std::cout << "y: "; display2(y);
}

void display1(const IntVector& vec) {
    if (vec.getSize() == 0) {
        std::cout << '\n';
        return;
    }
    std::cout << vec.getAt(0);
    for (int i = 1; i < vec.getSize(); ++i)
        std::cout << ", " << vec.getAt(i);
    std::cout << '\n';
}

void display2(const IntVector& vec) {
    if (vec.getSize() == 0) {
        std::cout << '\n';
        return;
    }
    auto it = vec.begin();
    std::cout << *it;
    for (it = vec.begin() + 1; it != vec.end(); ++it)
        std::cout << ", " << *it;
    std::cout << '\n';
}

タスク4:行列クラスの実装


#pragma once
#include <iostream>
#include <algorithm>
#include <cstdlib>

class Matrix {
public:
    Matrix(int rows, int cols, double value = 0);
    Matrix(int size, double value = 0);
    Matrix(const Matrix& other);
    ~Matrix();
    void setData(const double* values, int size);
    void clear();
    const double& at(int row, int col) const;
    double& at(int row, int col);
    int getRows() const;
    int getCols() const;
    void print() const;
private:
    int rows_;
    int cols_;
    double* data_;
};

#include "matrix.hpp"
#include <iostream>
#include <cstring>

Matrix::Matrix(int rows, int cols, double value)
    : rows_(rows), cols_(cols), data_(new double[rows * cols]) {
    if (rows <= 0 || cols <= 0) {
        std::cerr << "エラー: 無効な行列サイズ" << std::endl;
        std::exit(1);
    }
    for (int i = 0; i < rows * cols; ++i)
        data_[i] = value;
}

Matrix::Matrix(int size, double value)
    : Matrix(size, size, value) {}

Matrix::Matrix(const Matrix& other)
    : rows_(other.rows_), cols_(other.cols_), data_(new double[rows_ * cols_]) {
    std::memcpy(data_, other.data_, rows_ * cols_ * sizeof(double));
}

Matrix::~Matrix() {
    delete[] data_;
}

void Matrix::setData(const double* values, int size) {
    if (size != rows_ * cols_) {
        std::cerr << "エラー: サイズ不一致" << std::endl;
        std::exit(1);
    }
    std::memcpy(data_, values, size * sizeof(double));
}

void Matrix::clear() {
    for (int i = 0; i < rows_ * cols_; ++i)
        data_[i] = 0.0;
}

const double& Matrix::at(int row, int col) const {
    if (row < 0 || row >= rows_ || col < 0 || col >= cols_) {
        std::cerr << "エラー: インデックス範囲外" << std::endl;
        std::exit(1);
    }
    return data_[row * cols_ + col];
}

double& Matrix::at(int row, int col) {
    if (row < 0 || row >= rows_ || col < 0 || col >= cols_) {
        std::cerr << "エラー: インデックス範囲外" << std::endl;
        std::exit(1);
    }
    return data_[row * cols_ + col];
}

int Matrix::getRows() const {
    return rows_;
}

int Matrix::getCols() const {
    return cols_;
}

void Matrix::print() const {
    for (int i = 0; i < rows_; ++i) {
        std::cout << at(i, 0);
        for (int j = 1; j < cols_; ++j)
            std::cout << ", " << at(i, j);
        std::cout << '\n';
    }
}

タスク5:連絡先管理アプリケーション


#pragma once
#include <iostream>
#include <string>

class Contact {
public:
    Contact(const std::string& name, const std::string& phone);
    const std::string& getName() const;
    const std::string& getPhone() const;
    void show() const;
private:
    std::string name_;
    std::string phone_;
};

#pragma once
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include "contact.hpp"

class ContactBook {
public:
    void add(const std::string& name, const std::string& phone);
    void remove(const std::string& name);
    void search(const std::string& name) const;
    void display() const;
    size_t count() const;
private:
    int findIndex(const std::string& name) const;
    void sortByName();
private:
    std::vector<Contact> contacts_;
};

#include "contactbook.hpp"

void test() {
    ContactBook book;
    std::cout << "1. 連絡先を追加\n";
    book.add("Bob", "18199357253");
    book.add("Alice", "17300886371");
    book.add("Linda", "18184538072");
    book.add("Alice", "17300886371");
    std::cout << "\n2. 連絡先を表示\n";
    std::cout << "合計: " << book.count() << "件\n";
    book.display();
    std::cout << "\n3. 連絡先を検索\n";
    book.search("Bob");
    book.search("David");
    std::cout << "\n4. 連絡先を削除\n";
    book.remove("Bob");
    book.remove("David");
}

int main() {
    test();
}

まとめ

C++におけるオブジェクト指向設計は、単なる構文の利用ではなく、問題をオブジェクトとして抽象化する思考プロセスです。RAII原則を活用したリソース管理により、メモリ漏れやエラーを防ぎつつ、パフォーマンスを維持できます。また、インターフェース設計の際には、カプセル化を適切に維持し、公開範囲を最小限に抑えることで、コードの保守性と拡張性を向上させることができます。

タグ: C++ RAII オブジェクト指向設計 メモリ管理 std::vector

6月3日 21:30 投稿