C++におけるアクセス指定子の効果的な設計ガイド

C++プログラミングにおいて、クラス設計は非常に重要な要素です。特にアクセス指定子(public, private, protected)は、クラスのメンバ変数やメソッドのアクセスレベルを制御するための重要なツールです。本記事では、各アクセス指定子の基本的な使い方から、効果的な設計方法、さらに具体的な応用例や演習問題を通じて理解を深めていきます。

目次

アクセス指定子の基本

C++のアクセス指定子には、public、private、protectedの3種類があります。それぞれの役割と使い方を理解することは、堅牢で保守しやすいコードを書くために重要です。

public

public指定子は、クラスのメンバやメソッドをクラス外からもアクセス可能にします。一般的に、クラスのインターフェースを定義する際に使用されます。

class MyClass {
public:
    int publicVar;
    void publicMethod() {
        // 公開メソッド
    }
};

private

private指定子は、クラスのメンバやメソッドをクラス外からはアクセスできなくします。カプセル化を実現し、内部の実装を隠蔽するために使用されます。

class MyClass {
private:
    int privateVar;
    void privateMethod() {
        // 非公開メソッド
    }
};

protected

protected指定子は、クラス自身およびその派生クラスからはアクセス可能ですが、その他の外部からはアクセスできません。継承を利用する際に便利です。

class MyClass {
protected:
    int protectedVar;
    void protectedMethod() {
        // 保護されたメソッド
    }
};

アクセス指定子を適切に使い分けることで、クラスの設計がシンプルかつ堅牢になります。次の項目では、それぞれのアクセス指定子の使用例と注意点について詳しく見ていきます。

publicの使用例と注意点

public指定子を使うことで、クラスのメンバやメソッドを外部から直接アクセスできるようになります。これにより、クラスの機能を外部に提供するインターフェースとして機能させることができます。

publicの使用例

public指定子は、クラスの利用者が直接呼び出す必要のあるメソッドやアクセスすべきメンバ変数に使用します。

class Person {
public:
    std::string name;
    int age;

    void introduce() {
        std::cout << "My name is " << name << " and I am " << age << " years old." << std::endl;
    }
};

int main() {
    Person person;
    person.name = "John";
    person.age = 30;
    person.introduce();
    return 0;
}

この例では、Personクラスのnameとageメンバ変数、およびintroduceメソッドがpublicとして定義されています。これにより、main関数内で直接アクセスおよび使用できます。

publicの注意点

public指定子を多用すると、クラスの内部実装が外部から容易に変更されてしまう可能性があります。これにより、クラスの一貫性や安全性が損なわれることがあります。

  • 無制限なアクセス:publicメンバはどこからでもアクセス可能なため、意図しない場所からの変更が発生するリスクがあります。
  • カプセル化の欠如:クラスの内部状態を保護できないため、カプセル化の概念に反することになります。

改善策

publicメンバの使用を最小限に抑え、必要なインターフェースだけを公開することで、クラスの一貫性と安全性を保つことができます。getterやsetterメソッドを利用して、内部データへのアクセスを制御することも一つの方法です。

class Person {
private:
    std::string name;
    int age;

public:
    void setName(std::string newName) {
        name = newName;
    }

    void setAge(int newAge) {
        age = newAge;
    }

    void introduce() {
        std::cout << "My name is " << name << " and I am " << age << " years old." << std::endl;
    }
};

このようにすることで、nameやageの直接アクセスを防ぎ、クラスの内部状態を保護できます。次の項目では、private指定子の使用例と注意点について詳しく説明します。

privateの使用例と注意点

private指定子を使うことで、クラスのメンバやメソッドをクラスの外部からアクセスできなくなります。これにより、クラスの内部状態を保護し、カプセル化を実現します。

privateの使用例

private指定子は、クラス内部でのみ使用するデータやメソッドに使用します。これにより、クラスの利用者から内部実装を隠蔽できます。

class BankAccount {
private:
    double balance;

    void logTransaction(double amount) {
        // トランザクションの記録を行う
    }

public:
    BankAccount(double initialBalance) : balance(initialBalance) {}

    void deposit(double amount) {
        balance += amount;
        logTransaction(amount);
    }

    void withdraw(double amount) {
        if (amount <= balance) {
            balance -= amount;
            logTransaction(-amount);
        } else {
            std::cout << "Insufficient funds." << std::endl;
        }
    }

    double getBalance() const {
        return balance;
    }
};

int main() {
    BankAccount account(1000);
    account.deposit(500);
    account.withdraw(200);
    std::cout << "Balance: $" << account.getBalance() << std::endl;
    return 0;
}

この例では、BankAccountクラスのbalanceメンバ変数とlogTransactionメソッドがprivateとして定義されています。これにより、外部から直接アクセスすることができず、クラスの内部状態を保護します。

privateの注意点

private指定子を多用すると、クラスの柔軟性が低下し、必要な場合に外部からアクセスできなくなることがあります。特に、テストやデバッグの際に不便になることがあります。

  • アクセス制御の厳格化:privateメンバは完全に隠蔽されるため、必要に応じたアクセスが困難になることがあります。
  • テストの難しさ:テストコードから直接アクセスできないため、メソッドの動作確認が難しくなることがあります。

改善策

必要に応じて、getterやsetterメソッドを導入し、privateメンバへのアクセスを適切に制御することが推奨されます。これにより、内部状態を保護しながら、外部からの必要なアクセスを可能にします。

class BankAccount {
private:
    double balance;

public:
    BankAccount(double initialBalance) : balance(initialBalance) {}

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

    void withdraw(double amount) {
        if (amount <= balance) {
            balance -= amount;
        } else {
            std::cout << "Insufficient funds." << std::endl;
        }
    }

    double getBalance() const {
        return balance;
    }
};

このように、getterメソッドを利用して、外部からbalanceにアクセスできるようにすることで、クラスの柔軟性を保ちながら、内部状態を保護できます。次の項目では、protected指定子の使用例と注意点について詳しく説明します。

protectedの使用例と注意点

protected指定子を使うことで、クラスのメンバやメソッドをそのクラス自身および派生クラスからはアクセス可能にし、その他の外部からはアクセスできなくします。これにより、継承を利用する際に便利です。

protectedの使用例

protected指定子は、クラスの派生クラスがアクセスする必要があるが、外部からは隠蔽したいメンバやメソッドに使用します。

class Animal {
protected:
    std::string name;

public:
    Animal(std::string animalName) : name(animalName) {}

    void speak() {
        std::cout << "My name is " << name << std::endl;
    }
};

class Dog : public Animal {
public:
    Dog(std::string dogName) : Animal(dogName) {}

    void bark() {
        std::cout << name << " says: Woof!" << std::endl;
    }
};

int main() {
    Dog myDog("Buddy");
    myDog.speak();
    myDog.bark();
    return 0;
}

この例では、Animalクラスのnameメンバ変数がprotectedとして定義されています。これにより、Dogクラス(Animalクラスを継承)のインスタンスはnameにアクセスできますが、外部のコードからは直接アクセスできません。

protectedの注意点

protected指定子を多用すると、設計が複雑になりやすく、クラスの依存関係が増えることでメンテナンスが難しくなることがあります。

  • 設計の複雑化:protectedメンバは派生クラスからアクセスできるため、クラス間の依存関係が増加し、設計が複雑になる可能性があります。
  • セキュリティリスク:protectedメンバは派生クラスからアクセスできるため、不適切な使用によるセキュリティリスクが存在します。

改善策

protectedメンバを使用する際は、派生クラスの設計を慎重に行い、必要最小限のアクセス範囲に留めることが重要です。また、適切なドキュメントを用意し、コードの可読性とメンテナンス性を高めることも推奨されます。

class Animal {
private:
    std::string name;

protected:
    void setName(std::string newName) {
        name = newName;
    }

public:
    Animal(std::string animalName) : name(animalName) {}

    void speak() {
        std::cout << "My name is " << name << std::endl;
    }
};

class Dog : public Animal {
public:
    Dog(std::string dogName) : Animal(dogName) {}

    void bark() {
        std::cout << "Dog says: Woof!" << std::endl;
    }

    void rename(std::string newName) {
        setName(newName);
    }
};

このように、protectedメンバをprivateに変更し、必要に応じてprotectedメソッドでアクセスを提供することで、クラスの設計がシンプルになり、保守性が向上します。次の項目では、アクセス指定子の組み合わせについて詳しく説明します。

アクセス指定子の組み合わせ

C++では、アクセス指定子を組み合わせて使用することで、クラスの設計を柔軟かつ強固にすることができます。これにより、特定のメンバやメソッドに対して適切なアクセス制御を実現できます。

基本的な組み合わせのパターン

アクセス指定子を組み合わせることで、クラス内部と外部の両方で適切なアクセス制御を行うことができます。以下に、一般的なパターンを示します。

class SampleClass {
public:
    void publicMethod() {
        // 公開メソッド
    }

protected:
    void protectedMethod() {
        // 保護されたメソッド
    }

private:
    void privateMethod() {
        // 非公開メソッド
    }
};

この例では、SampleClass内にpublic、protected、privateの各メソッドが定義されています。それぞれのアクセス指定子に応じて、外部からのアクセスレベルが異なります。

応用的な組み合わせ例

アクセス指定子をうまく組み合わせることで、クラスの内部状態を厳格に管理しつつ、必要な機能を外部に提供することができます。

class Account {
public:
    Account(std::string owner, double initialBalance) 
        : ownerName(owner), balance(initialBalance) {}

    void deposit(double amount) {
        balance += amount;
        logTransaction("Deposit", amount);
    }

    void withdraw(double amount) {
        if (amount <= balance) {
            balance -= amount;
            logTransaction("Withdraw", amount);
        } else {
            std::cout << "Insufficient funds." << std::endl;
        }
    }

    double getBalance() const {
        return balance;
    }

protected:
    std::string getOwnerName() const {
        return ownerName;
    }

private:
    void logTransaction(const std::string &type, double amount) const {
        // トランザクションの記録
        std::cout << type << ": " << amount << std::endl;
    }

    std::string ownerName;
    double balance;
};

この例では、Accountクラスにおいて、public、protected、privateの各アクセス指定子を適切に組み合わせています。publicメソッドはクラスの外部からアクセス可能で、protectedメソッドは派生クラスからアクセス可能、privateメソッドはクラス内部でのみ使用されます。

クラスの設計におけるベストプラクティス

  • 最小限の公開:必要なメソッドやメンバのみをpublicにし、他はprotectedやprivateにすることで、クラスの一貫性と安全性を保ちます。
  • カプセル化の維持:内部実装を隠蔽するために、メンバ変数はできるだけprivateにし、アクセス用のメソッドを提供します。
  • 適切な継承の設計:protectedメンバやメソッドを使って、派生クラスが必要な機能にアクセスできるようにしつつ、カプセル化を維持します。

アクセス指定子を効果的に組み合わせることで、クラスの設計が柔軟かつ強固になり、保守性も向上します。次の項目では、アクセス指定子によるカプセル化の実現について詳しく説明します。

アクセス指定子によるカプセル化の実現

カプセル化は、オブジェクト指向プログラミングにおいて重要な概念です。アクセス指定子を適切に使用することで、クラスの内部状態を隠蔽し、外部からの不正なアクセスを防ぎます。

カプセル化の基本

カプセル化は、データの保護と制御を行うために、クラスの内部状態を外部から隠すことです。これにより、クラスの利用者は必要なインターフェースを通じてのみクラスを操作できます。

class Employee {
private:
    std::string name;
    int age;
    double salary;

public:
    Employee(std::string empName, int empAge, double empSalary) 
        : name(empName), age(empAge), salary(empSalary) {}

    std::string getName() const {
        return name;
    }

    void setName(std::string newName) {
        name = newName;
    }

    int getAge() const {
        return age;
    }

    void setAge(int newAge) {
        if (newAge > 0) {
            age = newAge;
        }
    }

    double getSalary() const {
        return salary;
    }

    void setSalary(double newSalary) {
        if (newSalary >= 0) {
            salary = newSalary;
        }
    }
};

この例では、Employeeクラスのメンバ変数(name、age、salary)はprivateとして定義されています。これにより、クラスの外部から直接アクセスできず、専用のgetterおよびsetterメソッドを通じてのみアクセス可能です。

カプセル化の利点

  • データ保護:クラスの内部状態を外部から保護し、不正な操作を防ぎます。
  • 変更の影響範囲の縮小:内部実装を変更しても、外部インターフェースを維持することで、既存のコードへの影響を最小限に抑えます。
  • デバッグとメンテナンスの容易化:データへのアクセス方法を統一することで、バグの発見と修正が容易になります。

カプセル化の実践

カプセル化を実現するためには、以下のような設計を行います。

  1. メンバ変数をprivateにする:直接アクセスを防ぐために、すべてのメンバ変数をprivateとして定義します。
  2. publicメソッドを通じてアクセスを提供する:必要なインターフェース(getterおよびsetterメソッド)をpublicとして提供します。
  3. データの整合性を保つ:setterメソッド内でデータの整合性チェックを行い、不正な値の設定を防ぎます。
class Product {
private:
    std::string productName;
    double price;
    int stockQuantity;

public:
    Product(std::string name, double initialPrice, int quantity) 
        : productName(name), price(initialPrice), stockQuantity(quantity) {}

    std::string getProductName() const {
        return productName;
    }

    void setProductName(std::string newName) {
        productName = newName;
    }

    double getPrice() const {
        return price;
    }

    void setPrice(double newPrice) {
        if (newPrice >= 0) {
            price = newPrice;
        }
    }

    int getStockQuantity() const {
        return stockQuantity;
    }

    void setStockQuantity(int newQuantity) {
        if (newQuantity >= 0) {
            stockQuantity = newQuantity;
        }
    }
};

このように、カプセル化を実現することで、クラスのデータ保護と安全な操作が可能になります。次の項目では、継承とアクセス指定子について詳しく説明します。

継承とアクセス指定子

継承は、既存のクラス(基底クラスまたはスーパークラス)の特性を新しいクラス(派生クラスまたはサブクラス)に引き継ぐ機能です。アクセス指定子は、継承においてもそのアクセス制御のルールを適用し、クラス間の関係を制御します。

public継承

public継承では、基底クラスのpublicメンバは派生クラスでもpublicとして扱われ、protectedメンバは派生クラスでもprotectedとして扱われます。基底クラスのprivateメンバには、派生クラスから直接アクセスできません。

class Base {
public:
    int publicVar;

protected:
    int protectedVar;

private:
    int privateVar;
};

class Derived : public Base {
public:
    void accessMembers() {
        publicVar = 1;     // OK
        protectedVar = 2;  // OK
        // privateVar = 3; // エラー:アクセス不可
    }
};

protected継承

protected継承では、基底クラスのpublicおよびprotectedメンバは、派生クラスでprotectedとして扱われます。基底クラスのprivateメンバには、派生クラスから直接アクセスできません。

class Base {
public:
    int publicVar;

protected:
    int protectedVar;

private:
    int privateVar;
};

class Derived : protected Base {
public:
    void accessMembers() {
        publicVar = 1;     // OK
        protectedVar = 2;  // OK
        // privateVar = 3; // エラー:アクセス不可
    }
};

private継承

private継承では、基底クラスのpublicおよびprotectedメンバは、派生クラスでprivateとして扱われます。基底クラスのprivateメンバには、派生クラスから直接アクセスできません。

class Base {
public:
    int publicVar;

protected:
    int protectedVar;

private:
    int privateVar;
};

class Derived : private Base {
public:
    void accessMembers() {
        publicVar = 1;     // OK
        protectedVar = 2;  // OK
        // privateVar = 3; // エラー:アクセス不可
    }
};

継承の利点と注意点

  • コードの再利用:基底クラスの機能を派生クラスで再利用することで、コードの重複を減らし、メンテナンス性を向上させます。
  • ポリモーフィズムの実現:ポリモーフィズム(多態性)を実現するために、基底クラスのポインタや参照を使用して、派生クラスのオブジェクトを操作できます。
class Animal {
public:
    virtual void speak() const {
        std::cout << "Animal speaks" << std::endl;
    }
};

class Dog : public Animal {
public:
    void speak() const override {
        std::cout << "Dog barks" << std::endl;
    }
};

void makeAnimalSpeak(const Animal& animal) {
    animal.speak();
}

int main() {
    Animal genericAnimal;
    Dog myDog;
    makeAnimalSpeak(genericAnimal); // Animal speaks
    makeAnimalSpeak(myDog);         // Dog barks
    return 0;
}
  • 設計の複雑化:継承を多用すると、クラス間の依存関係が増え、設計が複雑になる可能性があります。適切な設計とドキュメントが重要です。
  • 基底クラスの設計:基底クラスは、派生クラスに継承されることを考慮して設計する必要があります。特に、デストラクタは仮想関数として宣言することが推奨されます。
class Base {
public:
    virtual ~Base() = default; // 仮想デストラクタ
};

継承とアクセス指定子を適切に使い分けることで、柔軟で保守性の高いクラス設計が可能になります。次の項目では、アクセス指定子の実践例について詳しく説明します。

アクセス指定子の実践例

アクセス指定子を効果的に使用することで、堅牢で保守しやすいクラス設計を実現できます。ここでは、具体的なコード例を用いてアクセス指定子の実践的な利用方法を紹介します。

実践例1: 銀行口座クラス

銀行口座を管理するクラスを設計し、アクセス指定子を使用してデータの保護とカプセル化を実現します。

class BankAccount {
private:
    std::string accountHolder;
    double balance;

    void logTransaction(const std::string &type, double amount) const {
        // トランザクションの記録
        std::cout << type << ": " << amount << std::endl;
    }

public:
    BankAccount(std::string holder, double initialBalance)
        : accountHolder(holder), balance(initialBalance) {}

    void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
            logTransaction("Deposit", amount);
        }
    }

    void withdraw(double amount) {
        if (amount > 0 && amount <= balance) {
            balance -= amount;
            logTransaction("Withdraw", amount);
        } else {
            std::cout << "Insufficient funds." << std::endl;
        }
    }

    double getBalance() const {
        return balance;
    }

    std::string getAccountHolder() const {
        return accountHolder;
    }
};

int main() {
    BankAccount account("Alice", 1000.0);
    account.deposit(500);
    account.withdraw(200);
    std::cout << "Balance: $" << account.getBalance() << std::endl;
    return 0;
}

この例では、BankAccountクラスのbalanceとaccountHolderはprivateとして定義されています。logTransactionメソッドもprivateとして定義し、外部からアクセスできないようにしています。publicメソッド(deposit、withdraw、getBalance、getAccountHolder)を通じてのみ、アカウントの操作や情報取得が可能です。

実践例2: 交通機関クラスの継承

交通機関を管理する基底クラスと、その派生クラスを設計し、継承とアクセス指定子の使い方を示します。

class Transport {
protected:
    int speed;

public:
    Transport(int initSpeed) : speed(initSpeed) {}

    virtual void move() const {
        std::cout << "Moving at speed: " << speed << " km/h" << std::endl;
    }

    virtual ~Transport() = default; // 仮想デストラクタ
};

class Car : public Transport {
private:
    std::string model;

public:
    Car(int initSpeed, std::string carModel) : Transport(initSpeed), model(carModel) {}

    void move() const override {
        std::cout << model << " is driving at speed: " << speed << " km/h" << std::endl;
    }

    void setModel(std::string newModel) {
        model = newModel;
    }

    std::string getModel() const {
        return model;
    }
};

int main() {
    Car myCar(120, "Toyota");
    myCar.move();
    myCar.setModel("Honda");
    myCar.move();
    return 0;
}

この例では、Transportクラスを基底クラスとし、その派生クラスとしてCarを設計しています。Transportクラスのspeedメンバはprotectedとして定義し、派生クラスからアクセスできるようにしています。Carクラスのmodelメンバはprivateとして定義し、getterおよびsetterメソッドを通じてアクセスします。

実践例3: ユーザー管理システム

ユーザー管理システムを設計し、アクセス指定子を用いてセキュリティとデータ保護を実現します。

class User {
private:
    std::string username;
    std::string passwordHash;

public:
    User(std::string uname, std::string pHash)
        : username(uname), passwordHash(pHash) {}

    std::string getUsername() const {
        return username;
    }

    bool authenticate(std::string pHash) const {
        return passwordHash == pHash;
    }

    void setPasswordHash(std::string newHash) {
        passwordHash = newHash;
    }
};

class Admin : public User {
private:
    std::vector<std::string> privileges;

public:
    Admin(std::string uname, std::string pHash)
        : User(uname, pHash) {}

    void addPrivilege(std::string privilege) {
        privileges.push_back(privilege);
    }

    void showPrivileges() const {
        std::cout << "Admin Privileges: ";
        for (const auto &priv : privileges) {
            std::cout << priv << " ";
        }
        std::cout << std::endl;
    }
};

int main() {
    User normalUser("john_doe", "hashed_password");
    Admin adminUser("admin", "admin_hashed_password");

    adminUser.addPrivilege("ALL_ACCESS");
    adminUser.addPrivilege("MODIFY_USERS");

    std::cout << "Username: " << normalUser.getUsername() << std::endl;
    std::cout << "Admin Username: " << adminUser.getUsername() << std::endl;
    adminUser.showPrivileges();

    return 0;
}

この例では、Userクラスを基底クラスとし、その派生クラスとしてAdminを設計しています。UserクラスのusernameとpasswordHashはprivateとして定義し、セキュリティを保護しています。Adminクラスは、Userクラスの機能を継承しつつ、追加の特権管理機能を実装しています。

これらの実践例を通じて、アクセス指定子を適切に使用することで、安全で保守性の高いクラス設計を実現できます。次の項目では、応用例と演習問題について詳しく説明します。

応用例と演習問題

アクセス指定子の理解を深め、実際の開発で応用できるように、いくつかの応用例と演習問題を紹介します。これらの演習を通じて、アクセス指定子の使い方をさらにマスターしましょう。

応用例1: 図形クラスの設計

以下の図形クラスの階層を設計し、アクセス指定子を適切に使ってください。

class Shape {
protected:
    double area;

public:
    virtual void calculateArea() = 0; // 純粋仮想関数
    double getArea() const {
        return area;
    }
};

class Circle : public Shape {
private:
    double radius;

public:
    Circle(double r) : radius(r) {}

    void calculateArea() override {
        area = 3.14159 * radius * radius;
    }

    double getRadius() const {
        return radius;
    }

    void setRadius(double r) {
        radius = r;
    }
};

class Rectangle : public Shape {
private:
    double width, height;

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

    void calculateArea() override {
        area = width * height;
    }

    double getWidth() const {
        return width;
    }

    double getHeight() const {
        return height;
    }

    void setWidth(double w) {
        width = w;
    }

    void setHeight(double h) {
        height = h;
    }
};

この例では、Shapeクラスを基底クラスとし、CircleおよびRectangleクラスがこれを継承しています。各クラスでアクセス指定子を適切に使用し、内部データを保護しつつ必要な操作を実装しています。

応用例2: スマートデバイスクラスの設計

スマートデバイスを管理するクラスを設計し、アクセス指定子を使用してデータ保護とカプセル化を実現してください。

class SmartDevice {
private:
    std::string deviceName;
    std::string ipAddress;

protected:
    bool powerStatus;

public:
    SmartDevice(std::string name, std::string ip) 
        : deviceName(name), ipAddress(ip), powerStatus(false) {}

    std::string getDeviceName() const {
        return deviceName;
    }

    std::string getIpAddress() const {
        return ipAddress;
    }

    bool isPoweredOn() const {
        return powerStatus;
    }

    void togglePower() {
        powerStatus = !powerStatus;
    }
};

class SmartLight : public SmartDevice {
private:
    int brightness;

public:
    SmartLight(std::string name, std::string ip, int initialBrightness) 
        : SmartDevice(name, ip), brightness(initialBrightness) {}

    void setBrightness(int newBrightness) {
        if (newBrightness >= 0 && newBrightness <= 100) {
            brightness = newBrightness;
        }
    }

    int getBrightness() const {
        return brightness;
    }
};

class SmartThermostat : public SmartDevice {
private:
    double temperature;

public:
    SmartThermostat(std::string name, std::string ip, double initialTemperature) 
        : SmartDevice(name, ip), temperature(initialTemperature) {}

    void setTemperature(double newTemperature) {
        temperature = newTemperature;
    }

    double getTemperature() const {
        return temperature;
    }
};

この例では、SmartDeviceクラスを基底クラスとし、SmartLightおよびSmartThermostatクラスがこれを継承しています。各クラスでアクセス指定子を適切に使用し、デバイスの属性を管理しています。

演習問題

  1. クラス設計の演習
  • 新しいクラスLibraryBookを設計し、以下の属性を持つようにしてください:タイトル(title)、著者(author)、ISBN番号(ISBN)、貸出状況(isCheckedOut)。
  • titleとauthorはpublic、ISBNはprivate、isCheckedOutはprotectedとします。
  • 貸出状況を操作するためのメソッドcheckOut()とreturnBook()を実装してください。
  1. 継承とアクセス指定子の演習
  • 基底クラスVehicleを設計し、速度(speed)と色(color)の属性を持たせてください。速度はprotected、色はprivateとします。
  • Vehicleクラスを継承するCarクラスを設計し、新たにブランド(brand)属性をpublicで追加してください。
  • Carクラスに、速度を設定するsetSpeed()メソッドを実装し、Vehicleクラスの速度を操作してください。
  1. ポリモーフィズムの演習
  • 基底クラスAnimalを設計し、speak()という純粋仮想関数を定義してください。
  • Animalクラスを継承するDogクラスとCatクラスを設計し、それぞれspeak()メソッドを実装してください。
  • Animal型のポインタを使って、DogおよびCatオブジェクトのspeak()メソッドを呼び出すコードを書いてください。

これらの演習問題を通じて、アクセス指定子の効果的な使用方法をさらに深く理解し、実践力を養いましょう。次の項目では、本記事のまとめを行います。

まとめ

本記事では、C++におけるアクセス指定子(public、private、protected)の基本的な使い方から、効果的な設計方法、具体的な応用例、そして演習問題を通じてその理解を深めました。

アクセス指定子を適切に使用することで、クラスの内部状態を保護し、データのカプセル化を実現することができます。また、継承におけるアクセス制御も理解することで、柔軟で保守性の高いクラス設計が可能になります。

これらの知識と技術を活用して、堅牢で効率的なC++プログラムを設計・実装してください。アクセス指定子の正しい使用は、コードの可読性と保守性を向上させるための重要なステップです。

コメント

コメントする

目次