15_クラス設計演習問題

はじめに

ここまで学んだ「クラス」「コンストラクタ」「アクセス修飾子」をしっかり理解するため、コピー元コードを改良する演習問題を提示します。新しい知識は不要なので、これまでの復習として取り組みましょう。

理解度チェック

問題15-1

次のコードについて、a.balance = -100; のような外部からの不正アクセスを防ぐには何をすればよいですか?

class Account {
public:
    int balance;
    void deposit(int amount) { balance += amount; }
};
解説を表示 正解:イ
private にすることでクラス外部からの直接アクセスを禁止できます(カプセル化)。操作は deposit() などのメソッド経由にします。これにより不正な値の設定を防げます。

問題15-2

次のコードを実行したとき、何が表示されますか?

#include <iostream>
using namespace std;

class Account {
private:
    int balance;
public:
    Account(int initial) : balance(initial) {}
    bool paycheck(int amount) { return balance >= amount; }
    void pay(int amount) { balance -= amount; cout << amount << "円出金" << endl; }
    void show() { cout << "残高: " << balance << "円" << endl; }
};

int main() {
    Account a(500);
    if (a.paycheck(200)) a.pay(200);
    else cout << "出金失敗" << endl;
    a.show();
    return 0;
}
解説を表示 正解:ウ
初期残高 500 円で、paycheck(200)500 >= 200 が true なので pay(200) が呼ばれます。残高は 500 - 200 = 300 円になります。出力は "200円出金" → "残高: 300円" です。

問題15-3

次のコードで r.width = 10; のような行は必要でしょうか?

class Rectangle {
public:
    int width;
    int height;
    int area() { return width * height; }
};

int main() {
    Rectangle r;
    r.width = 5;
    r.height = 3;
    cout << r.area() << endl;
}
解説を表示 正解:ア
コンストラクタで初期化し、widthheightprivate にすることで、外部から不正な値を設定されるリスクがなくなります。これがカプセル化の考え方です。

問題15-4

次のコードの実行結果はどれですか?

#include <iostream>
using namespace std;

class Product {
private:
    string name;
    int price;
public:
    Product(string n, int p) : name(n), price(p) {}
    void show() { cout << name << " は " << price << " 円です。" << endl; }
};

int main() {
    Product p("みかん", 80);
    p.show();
    return 0;
}
解説を表示 正解:ア
コンストラクタの初期化リストで name("みかん")price(80) と設定されます。show() はクラスのメンバ関数なので private メンバにアクセスでき、"みかん は 80 円です。" と出力されます。

コーディング演習

演習1:Accountクラスのリファクタリング

15_01_account.cpp を作成し、次の元のコードを入力して動作確認した後、改善指示に従って書き換えましょう。
// account.cpp(元のコード)
#include <iostream>
using namespace std;

class Account {
public:
    int balance;

    void deposit(int amount) {
        balance += amount;
    }

    void show() {
        cout << "残高: " << balance << "円" << endl;
    }
};

int main() {
    Account a;
    a.balance = 0;
    a.deposit(500);
    a.show();

    return 0;
}

改善指示:

  1. balanceprivate にする
  2. コンストラクタで初期残高を設定できるようにする
  3. deposit()show() はそのまま使えるようにする
  4. 出金用の pay(int) 関数を作る
  5. 出金可能かどうかを確認する bool paycheck(int) 関数を作る
  6. main() で残高500円からスタートし、出金可能なら支払いし、そうでなければ失敗メッセージを表示し、最後に残高を表示する
解答例を表示
// 15_account_refactor.cpp
#include <iostream>
using namespace std;

class Account {
private:
    int balance;

public:
    Account(int initial) {
        balance = initial;
    }

    void deposit(int amount) {
        balance += amount;
    }

    void pay(int amount) {
        balance -= amount;
        cout << amount << "円を出金しました。" << endl;
    }

    bool paycheck(int amount) {
        return balance >= amount;
    }

    void show() {
        cout << "残高: " << balance << "円" << endl;
    }
};

int main() {
    Account a(500);
    a.deposit(300);

    if (a.paycheck(200)) {
        a.pay(200);
    } else {
        cout << "200円の出金に失敗しました。" << endl;
    }

    if (a.paycheck(700)) {
        a.pay(700);
    } else {
        cout << "700円の出金に失敗しました。" << endl;
    }

    a.show();

    return 0;
}
解説
balanceprivate にしてカプセル化しました。出金可能性を事前に paycheck() で判定し、if 文で分岐する構造としました。

演習2:Rectangleクラスのリファクタリング

15_02_rectangle.cpp を新規作成し、次の元のコードを改善しましょう。
// rectangle.cpp(元のコード)
#include <iostream>
using namespace std;

class Rectangle {
public:
    int width;
    int height;

    int area() {
        return width * height;
    }
};

int main() {
    Rectangle r;
    r.width = 5;
    r.height = 3;
    cout << "面積: " << r.area() << endl;

    return 0;
}

改善指示:

  1. widthheightprivate に変更する
  2. デフォルトサイズ(1×1)と任意サイズの両方に対応するコンストラクタをオーバーロードする
  3. area() 関数はそのまま使えるようにする
  4. main() でデフォルトサイズとサイズ指定の両方のオブジェクトを生成して面積を表示する
解答例を表示
// 15_rectangle_refactor.cpp
#include <iostream>
using namespace std;

class Rectangle {
private:
    int width;
    int height;

public:
    Rectangle() : width(1), height(1) {}
    Rectangle(int w, int h) : width(w), height(h) {}

    int area() {
        return width * height;
    }
};

int main() {
    Rectangle r1;
    Rectangle r2(5, 3);

    cout << "r1の面積: " << r1.area() << endl;
    cout << "r2の面積: " << r2.area() << endl;

    return 0;
}

演習3:Productクラスのリファクタリング

15_03_product.cpp を作成し、次の元のコードを改善しましょう。
// product.cpp(元のコード)
#include <iostream>
using namespace std;

class Product {
public:
    string name;
    int price;

    void show() {
        cout << name << " は " << price << " 円です。" << endl;
    }
};

int main() {
    Product p;
    p.name = "りんご";
    p.price = 150;
    p.show();

    return 0;
}

改善指示:

  1. namepriceprivate に変更する
  2. コンストラクタで商品名と価格を指定できるようにする(初期化リストを使う)
  3. show() 関数はそのまま使えるようにする
  4. main() でインスタンスを生成し、正しく表示されることを確認する
解答例を表示
// 15_product_refactor.cpp
#include <iostream>
using namespace std;

class Product {
private:
    string name;
    int price;

public:
    Product(string n, int p) : name(n), price(p) {}

    void show() {
        cout << name << " は " << price << " 円です。" << endl;
    }
};

int main() {
    Product p("りんご", 150);
    p.show();

    return 0;
}

まとめ