C++のアクセス指定子public, private, protectedの基本と使い方完全ガイド

C++,アクセス指定子,public,private,protected,プログラミング,カプセル化,クラス,継承果たします。アクセス指定子を適切に使用することで、クラスの設計が効率的かつ安全になります。本記事では、C++のアクセス指定子であるpublic、private、protectedの基本的な使い方と、その具体的な例について詳しく解説します。アクセス指定子の基本を理解し、実際のコードにどのように適用するかを学びましょう。

目次

アクセス指定子とは

アクセス指定子とは、クラス内のメンバー変数やメソッドのアクセス範囲を制御するためのキーワードです。C++では、主にpublic、private、protectedの3つのアクセス指定子が用意されており、それぞれの役割に応じてアクセス範囲を設定します。

アクセス指定子の役割

アクセス指定子の役割は、クラスの外部からのアクセスを制御し、データのカプセル化を実現することです。これにより、不正なアクセスや意図しない変更を防ぎ、コードの安全性と可読性を向上させます。

public

public指定子は、メンバー変数やメソッドをクラスの外部から自由にアクセスできるようにします。主に、外部に公開する必要があるメンバーに使用します。

private

private指定子は、メンバー変数やメソッドをクラスの内部からのみアクセスできるようにします。外部からの直接アクセスを防ぐため、データの隠蔽(カプセル化)に役立ちます。

protected

protected指定子は、メンバー変数やメソッドをクラス自身とその派生クラスからのみアクセスできるようにします。クラスの継承に関連する場合に利用されます。

publicの使い方と例

public指定子を使用すると、クラス内のメンバー変数やメソッドに外部から自由にアクセスできます。これは、クラスのインターフェース部分として利用されることが多いです。

public指定子の使い方

public指定子は、メンバー変数やメソッドの前に記述します。以下の例は、public指定子を使った基本的なクラス定義です。

#include <iostream>

class Example {
public:
    int publicVar;  // publicメンバー変数

    void publicMethod() {  // publicメソッド
        std::cout << "This is a public method." << std::endl;
    }
};

int main() {
    Example ex;
    ex.publicVar = 10;  // publicメンバー変数へのアクセス
    ex.publicMethod();  // publicメソッドの呼び出し

    return 0;
}

public指定子の利点

  1. 外部アクセスの許可: publicメンバーはクラスの外部からアクセス可能で、クラスの機能を外部に提供します。
  2. インターフェースの明示: publicメンバーはクラスのインターフェース部分を構成し、ユーザーにとって利用可能なメソッドや変数を明確にします。

public指定子の使用例

次に、public指定子を使用して作成されたクラスの具体的な使用例を示します。

#include <iostream>
#include <string>

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 Doe";  // publicメンバー変数へのアクセス
    person.age = 30;  // publicメンバー変数へのアクセス
    person.introduce();  // publicメソッドの呼び出し

    return 0;
}

この例では、Personクラスのnameageがpublicメンバーとして定義されており、introduceメソッドもpublicメソッドとして外部から呼び出せるようになっています。これにより、クラスの利用者は簡単にPersonオブジェクトの情報にアクセスし、操作することができます。

privateの使い方と例

private指定子を使用すると、クラス内のメンバー変数やメソッドはクラスの外部から直接アクセスできなくなります。これは、データの隠蔽(カプセル化)を実現し、クラス内部の実装を保護するために重要です。

private指定子の使い方

private指定子は、メンバー変数やメソッドの前に記述します。以下の例は、private指定子を使った基本的なクラス定義です。

#include <iostream>

class Example {
private:
    int privateVar;  // privateメンバー変数

    void privateMethod() {  // privateメソッド
        std::cout << "This is a private method." << std::endl;
    }

public:
    void setPrivateVar(int value) {
        privateVar = value;  // privateメンバー変数への間接的アクセス
    }

    int getPrivateVar() {
        return privateVar;  // privateメンバー変数の取得
    }
};

int main() {
    Example ex;
    // ex.privateVar = 10;  // エラー: privateメンバー変数への直接アクセスは不可
    // ex.privateMethod();  // エラー: privateメソッドの直接呼び出しは不可

    ex.setPrivateVar(10);  // publicメソッドを通じてprivateメンバー変数にアクセス
    std::cout << "Private variable value: " << ex.getPrivateVar() << std::endl;

    return 0;
}

private指定子の利点

  1. データの保護: privateメンバーはクラスの外部からアクセスできないため、データの一貫性と安全性が確保されます。
  2. 内部実装の隠蔽: クラスの内部実装を外部に隠すことで、インターフェースと実装を分離し、コードの保守性を向上させます。

private指定子の使用例

次に、private指定子を使用して作成されたクラスの具体的な使用例を示します。

#include <iostream>
#include <string>

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

public:
    void setName(const std::string& newName) {
        name = newName;  // privateメンバー変数への間接的アクセス
    }

    std::string getName() const {
        return name;  // privateメンバー変数の取得
    }

    void setAge(int newAge) {
        if (newAge >= 0) {  // 年齢が有効な値かどうかをチェック
            age = newAge;
        }
    }

    int getAge() const {
        return age;  // privateメンバー変数の取得
    }

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

int main() {
    Person person;
    person.setName("John Doe");  // publicメソッドを通じてprivateメンバー変数にアクセス
    person.setAge(30);  // publicメソッドを通じてprivateメンバー変数にアクセス
    person.introduce();  // publicメソッドの呼び出し

    return 0;
}

この例では、Personクラスのnameageがprivateメンバーとして定義されており、外部からは直接アクセスできません。代わりに、setNamegetNamesetAge、およびgetAgeといったpublicメソッドを通じてアクセスします。これにより、データの保護が強化され、クラスの利用者が適切な方法でメンバー変数にアクセスすることを保証します。

protectedの使い方と例

protected指定子を使用すると、クラス内のメンバー変数やメソッドはクラス自身およびその派生クラスからアクセス可能になります。これは、継承を利用したクラス設計において重要な役割を果たします。

protected指定子の使い方

protected指定子は、メンバー変数やメソッドの前に記述します。以下の例は、protected指定子を使った基本的なクラス定義です。

#include <iostream>

class Base {
protected:
    int protectedVar;  // protectedメンバー変数

    void protectedMethod() {  // protectedメソッド
        std::cout << "This is a protected method." << std::endl;
    }
};

class Derived : public Base {
public:
    void accessProtected() {
        protectedVar = 10;  // 派生クラスからprotectedメンバー変数へのアクセス
        protectedMethod();  // 派生クラスからprotectedメソッドの呼び出し
    }
};

int main() {
    Derived d;
    d.accessProtected();

    return 0;
}

protected指定子の利点

  1. 継承での利用: protectedメンバーは派生クラスからアクセスできるため、継承関係にあるクラス間でのデータ共有が可能です。
  2. 安全なデータ管理: privateよりも柔軟にデータを管理しつつ、publicほど開放的ではないため、適度なカプセル化を維持できます。

protected指定子の使用例

次に、protected指定子を使用して作成されたクラスの具体的な使用例を示します。

#include <iostream>
#include <string>

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

public:
    Person(const std::string& newName, int newAge) : name(newName), age(newAge) {}

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

class Employee : public Person {
private:
    std::string position;

public:
    Employee(const std::string& newName, int newAge, const std::string& newPosition)
        : Person(newName, newAge), position(newPosition) {}

    void displayEmployeeInfo() const {
        introduce();  // 基底クラスのprotectedメソッドへのアクセス
        std::cout << "I work as a " << position << "." << std::endl;
    }
};

int main() {
    Employee emp("Alice Smith", 28, "Software Engineer");
    emp.displayEmployeeInfo();

    return 0;
}

この例では、Personクラスのnameageがprotectedメンバーとして定義されており、派生クラスのEmployeeからアクセス可能です。Employeeクラスは、Personクラスを継承し、そのprotectedメンバーにアクセスしてdisplayEmployeeInfoメソッド内で使用しています。これにより、継承関係を活用して、データの共有とカプセル化を両立することができます。

アクセス指定子の使い分け

アクセス指定子を正しく使い分けることは、クラス設計の中で非常に重要です。それぞれの指定子には独自の特性と適用例があります。ここでは、public、private、protectedの使い分けについて具体的な例を用いて説明します。

publicの使いどころ

public指定子は、クラスのインターフェースとして外部に公開したいメンバーに使用します。主に以下のような場面で利用します。

  • 外部から直接アクセスして操作する必要があるメソッドや変数
  • クラスの利用者に提供する機能やサービス
  • クラスの状態を表示するためのメソッド
class Car {
public:
    void startEngine() {
        std::cout << "Engine started." << std::endl;
    }

    void stopEngine() {
        std::cout << "Engine stopped." << std::endl;
    }
};

privateの使いどころ

private指定子は、クラスの内部でのみ使用するメンバーに使用します。データの保護や内部ロジックの隠蔽に役立ちます。以下のような場面で利用します。

  • 外部からアクセスさせたくないデータ
  • 内部ロジックや補助的なメソッド
  • 一貫性を保つためのデータ
class Car {
private:
    int fuelLevel;

    void burnFuel() {
        if (fuelLevel > 0) {
            fuelLevel--;
            std::cout << "Burning fuel, level now: " << fuelLevel << std::endl;
        } else {
            std::cout << "Out of fuel." << std::endl;
        }
    }
};

protectedの使いどころ

protected指定子は、クラスとその派生クラス内でアクセスできるメンバーに使用します。クラスの継承関係でデータやメソッドを共有したい場合に有効です。

  • 継承されたクラスでアクセスさせたいメンバー
  • 派生クラスに対して提供する共通の機能
  • 基底クラスで実装し、派生クラスで利用するメソッド
class Car {
protected:
    int speed;

    void accelerate() {
        speed += 10;
        std::cout << "Speeding up, now: " << speed << " km/h" << std::endl;
    }
};

class SportsCar : public Car {
public:
    void turboBoost() {
        accelerate();
        speed += 20;
        std::cout << "Turbo boost, speed now: " << speed << " km/h" << std::endl;
    }
};

アクセス指定子の使い分けのまとめ

  • public: クラスのインターフェースとして外部に公開するメンバーに使用
  • private: クラス内部でのみ使用し、外部からのアクセスを防ぐメンバーに使用
  • protected: 継承関係にあるクラス間で共有するメンバーに使用

これらのアクセス指定子を正しく使い分けることで、クラスの設計が明確になり、コードの保守性と安全性が向上します。

応用例:クラスの継承とアクセス指定子

クラスの継承は、既存のクラスをベースに新しいクラスを作成する強力な手法です。継承において、アクセス指定子はどのメンバーが派生クラスからアクセス可能かを決定する重要な役割を果たします。

継承とアクセス指定子の関係

クラスの継承では、基底クラス(親クラス)のメンバーを派生クラス(子クラス)に引き継ぐことができますが、その際のアクセス範囲はアクセス指定子によって制御されます。

public継承

基底クラスのpublicメンバーとprotectedメンバーは、派生クラスでもそれぞれpublicおよびprotectedとして引き継がれます。privateメンバーは引き継がれませんが、基底クラスのメソッドを通じてアクセス可能です。

#include <iostream>

class Base {
public:
    int publicVar;
protected:
    int protectedVar;
private:
    int privateVar;

public:
    Base() : publicVar(1), protectedVar(2), privateVar(3) {}
    int getPrivateVar() const { return privateVar; }
};

class Derived : public Base {
public:
    void display() {
        std::cout << "Public Var: " << publicVar << std::endl;         // OK
        std::cout << "Protected Var: " << protectedVar << std::endl;   // OK
        // std::cout << "Private Var: " << privateVar << std::endl;   // エラー: privateメンバーにはアクセス不可
        std::cout << "Private Var (via method): " << getPrivateVar() << std::endl;  // OK: publicメソッドを通じてアクセス
    }
};

int main() {
    Derived d;
    d.display();
    return 0;
}

protected継承

基底クラスのpublicメンバーとprotectedメンバーは、派生クラスでprotectedとして引き継がれます。これにより、外部からのアクセスを防ぎつつ、派生クラス内でのアクセスが可能になります。

#include <iostream>

class Base {
public:
    int publicVar;
protected:
    int protectedVar;

public:
    Base() : publicVar(1), protectedVar(2) {}
};

class Derived : protected Base {
public:
    void display() {
        std::cout << "Public Var (now protected): " << publicVar << std::endl;  // OK
        std::cout << "Protected Var: " << protectedVar << std::endl;  // OK
    }
};

int main() {
    Derived d;
    d.display();
    // std::cout << d.publicVar << std::endl;  // エラー: publicVarは派生クラスでprotectedになったため、外部からアクセス不可
    return 0;
}

private継承

基底クラスのpublicメンバーとprotectedメンバーは、派生クラスでprivateとして引き継がれます。これにより、派生クラスの内部でのみアクセス可能となります。

#include <iostream>

class Base {
public:
    int publicVar;
protected:
    int protectedVar;

public:
    Base() : publicVar(1), protectedVar(2) {}
};

class Derived : private Base {
public:
    void display() {
        std::cout << "Public Var (now private): " << publicVar << std::endl;  // OK
        std::cout << "Protected Var (now private): " << protectedVar << std::endl;  // OK
    }
};

int main() {
    Derived d;
    d.display();
    // std::cout << d.publicVar << std::endl;  // エラー: publicVarは派生クラスでprivateになったため、外部からアクセス不可
    return 0;
}

まとめ

クラスの継承においてアクセス指定子を正しく理解し使い分けることは、コードの可読性や保守性に大きく影響します。public、protected、privateの違いを理解し、設計に応じて適切に利用しましょう。これにより、安全で拡張性のあるクラス設計が可能になります。

アクセス指定子を用いたカプセル化の実現

カプセル化とは、クラスの内部状態を隠蔽し、外部からの直接アクセスを制限することで、データの整合性を保つ手法です。アクセス指定子を活用することで、カプセル化を効果的に実現できます。

カプセル化の目的

カプセル化の主な目的は以下の通りです。

  • データの保護: 不正なアクセスや変更を防ぐ。
  • 実装の隠蔽: クラスの内部構造を隠し、外部に対してシンプルなインターフェースを提供する。
  • メンテナンス性の向上: 内部の変更が外部に影響を与えないようにする。

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

アクセス指定子を使って、クラスのデータメンバーをprivateにし、必要な操作のみをpublicメソッドとして提供することで、カプセル化を実現します。

#include <iostream>
#include <string>

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

public:
    BankAccount(const std::string& accNum, double initialBalance)
        : accountNumber(accNum), balance(initialBalance) {}

    // 残高の取得
    double getBalance() const {
        return balance;
    }

    // 預金メソッド
    void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
            std::cout << "Deposited: " << amount << ", New Balance: " << balance << std::endl;
        } else {
            std::cout << "Invalid deposit amount." << std::endl;
        }
    }

    // 引き出しメソッド
    void withdraw(double amount) {
        if (amount > 0 && amount <= balance) {
            balance -= amount;
            std::cout << "Withdrew: " << amount << ", New Balance: " << balance << std::endl;
        } else {
            std::cout << "Invalid withdraw amount or insufficient balance." << std::endl;
        }
    }
};

int main() {
    BankAccount account("123456789", 1000.0);

    account.deposit(500.0);  // 預金
    account.withdraw(200.0);  // 引き出し
    std::cout << "Current Balance: " << account.getBalance() << std::endl;  // 残高の取得

    // account.balance = 1000;  // エラー: privateメンバーには直接アクセスできない
    return 0;
}

カプセル化の利点

  1. データの整合性: 外部から直接データメンバーにアクセスできないため、不正な値が設定されるのを防げます。
  2. コードの保守性: 内部の実装を変更しても、外部に公開されたインターフェースが変わらなければ、外部コードに影響を与えません。
  3. 明確なインターフェース: クラスの利用者は、どの操作が可能かを明確に理解でき、誤用を防ぐことができます。

実際の応用

カプセル化は、銀行システムやユーザー情報管理など、データの保護が重要なシステムで特に有効です。以下は、ユーザー情報をカプセル化する例です。

#include <iostream>
#include <string>

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

public:
    User(const std::string& user, const std::string& pass)
        : username(user), password(pass) {}

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

    bool authenticate(const std::string& pass) const {
        return password == pass;
    }

    void setPassword(const std::string& oldPass, const std::string& newPass) {
        if (authenticate(oldPass)) {
            password = newPass;
            std::cout << "Password changed successfully." << std::endl;
        } else {
            std::cout << "Authentication failed. Password not changed." << std::endl;
        }
    }
};

int main() {
    User user("john_doe", "securepassword123");

    std::cout << "Username: " << user.getUsername() << std::endl;

    if (user.authenticate("securepassword123")) {
        std::cout << "Authentication successful." << std::endl;
    } else {
        std::cout << "Authentication failed." << std::endl;
    }

    user.setPassword("securepassword123", "newpassword456");

    return 0;
}

この例では、ユーザー名とパスワードがprivateメンバーとして定義されており、外部から直接アクセスできません。パスワードの変更や認証はpublicメソッドを通じて行われ、データの保護が実現されています。

演習問題

以下の演習問題を通じて、C++のアクセス指定子(public、private、protected)の理解を深めましょう。各問題には解答例も提供していますので、実際にコーディングして確認してみてください。

演習問題1: アクセス指定子の基本

次のクラスRectangleを完成させてください。幅と高さをprivateメンバーとして定義し、それらにアクセスするためのpublicメソッドを実装してください。

#include <iostream>

class Rectangle {
private:
    double width;
    double height;

public:
    // コンストラクタ
    Rectangle(double w, double h) : width(w), height(h) {}

    // 幅を設定するメソッド
    void setWidth(double w);

    // 高さを設定するメソッド
    void setHeight(double h);

    // 面積を計算するメソッド
    double area() const;
};

// メソッドの実装
void Rectangle::setWidth(double w) {
    width = w;
}

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

double Rectangle::area() const {
    return width * height;
}

int main() {
    Rectangle rect(5.0, 3.0);
    std::cout << "Area: " << rect.area() << std::endl;

    rect.setWidth(4.0);
    rect.setHeight(2.0);
    std::cout << "New Area: " << rect.area() << std::endl;

    return 0;
}

演習問題2: カプセル化の実践

クラスBankAccountを定義し、口座番号と残高をprivateメンバーとして実装してください。残高の入金、引き出し、および残高照会のpublicメソッドを追加してください。

#include <iostream>
#include <string>

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

public:
    // コンストラクタ
    BankAccount(const std::string& accNum, double initialBalance) 
        : accountNumber(accNum), balance(initialBalance) {}

    // 入金メソッド
    void deposit(double amount);

    // 引き出しメソッド
    void withdraw(double amount);

    // 残高照会メソッド
    double getBalance() const;
};

// メソッドの実装
void BankAccount::deposit(double amount) {
    if (amount > 0) {
        balance += amount;
        std::cout << "Deposited: " << amount << ", New Balance: " << balance << std::endl;
    } else {
        std::cout << "Invalid deposit amount." << std::endl;
    }
}

void BankAccount::withdraw(double amount) {
    if (amount > 0 && amount <= balance) {
        balance -= amount;
        std::cout << "Withdrew: " << amount << ", New Balance: " << balance << std::endl;
    } else {
        std::cout << "Invalid withdraw amount or insufficient balance." << std::endl;
    }
}

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

int main() {
    BankAccount account("123456789", 1000.0);

    account.deposit(500.0);
    account.withdraw(200.0);
    std::cout << "Current Balance: " << account.getBalance() << std::endl;

    return 0;
}

演習問題3: 継承とアクセス指定子

基底クラスPersonを定義し、その派生クラスStudentを作成してください。Personクラスには名前と年齢をprotectedメンバーとして定義し、Studentクラスでは学籍番号を追加してください。名前と年齢の取得メソッドも実装してください。

#include <iostream>
#include <string>

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

public:
    Person(const std::string& newName, int newAge) : name(newName), age(newAge) {}

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

    int getAge() const {
        return age;
    }
};

class Student : public Person {
private:
    std::string studentID;

public:
    Student(const std::string& newName, int newAge, const std::string& newID)
        : Person(newName, newAge), studentID(newID) {}

    std::string getStudentID() const {
        return studentID;
    }

    void displayInfo() const {
        std::cout << "Name: " << getName() << ", Age: " << getAge() << ", Student ID: " << studentID << std::endl;
    }
};

int main() {
    Student student("Alice", 20, "S12345");
    student.displayInfo();

    return 0;
}

これらの演習問題を解くことで、C++のアクセス指定子の実際の使用方法とカプセル化、継承の概念を深く理解することができます。

まとめ

本記事では、C++のアクセス指定子であるpublic、private、protectedの基本的な使い方とその役割について詳しく解説しました。アクセス指定子を正しく使い分けることで、クラスのデータ保護、実装の隠蔽、インターフェースの明確化を実現し、プログラムの安全性と保守性を向上させることができます。また、継承とアクセス指定子の関係についても学び、クラス設計の柔軟性と再利用性を高める方法を理解しました。

最後に、演習問題を通じて、実際にアクセス指定子を用いたカプセル化と継承を実践し、その効果を確認しました。これらの知識と技術を活用して、より堅牢で効率的なC++プログラムを作成してください。

コメント

コメントする

目次