閉じる

C++のpublic / private / protectedとは?アクセス修飾子超入門

C++でクラスを学ぶと必ず登場するのが、public / private / protectedという3つのアクセス修飾子です。

どれも短い英単語ですが、オブジェクト指向プログラミングを理解するうえで非常に重要な役割を持っています。

本記事では、初心者の方でもイメージしやすいように、図解と具体的なコード例を用いながら、3つのアクセス修飾子の違いと使い分けを丁寧に説明していきます。

C++のアクセス修飾子とは

アクセス修飾子のイメージ

C++におけるアクセス修飾子とは、クラスのメンバ(変数や関数)を、誰からどこまで見せるかを決めるための「公開レベルの設定」です。

具体的には次の3種類があります。

  • public … どこからでもアクセスできる
  • private … クラスの内部からしかアクセスできない
  • protected … クラス自身と、その派生クラスからはアクセスできるが、その他からはアクセスできない

この3つをうまく使うことで、大事なデータを守りつつ、必要な操作だけを外部に公開することができます。

なぜアクセス修飾子が必要なのか

アクセス修飾子がないと、誰でもクラス内部の変数を書き換えられてしまいます。

例えば、銀行口座の残高を表す変数があったとして、外部から自由に変更できてしまうと、勝手に残高を増やす、といった不正な操作も技術的には可能になってしまいます。

そこでC++では、大事なデータ(フィールド)はprivateで隠し、外部からは安全性を確保したpublicメソッドを通してしか操作させない、という設計が推奨されます。

これが「カプセル化」と呼ばれる考え方です。

publicとは何か

publicの基本的な意味

publicは「誰でも見てよい・使ってよい」メンバを宣言するためのアクセス修飾子です。

クラスの外からも、派生クラスの中からも、同じようにアクセスできます。

クラスの「外に公開するインターフェース」を定義するときに使うのがpublicです。

たとえば「値を設定する」「値を取得する」といったメソッドは、多くの場合publicになります。

publicの基本コード例

C++
#include <iostream>
#include <string>

class Person {
public: // ここから下は「公開」される

    // 名前を設定するpublicメソッド
    void setName(const std::string& newName) {
        name = newName; // privateなメンバを内部から変更
    }

    // 名前を取得するpublicメソッド
    std::string getName() const {
        return name; // privateなメンバを内部から参照
    }

private: // ここから下は「非公開」

    // 外部から直接触ってほしくないデータ
    std::string name;
};

int main() {
    Person p;

    // publicメソッドはクラスの外から呼び出せる
    p.setName("Taro");
    std::cout << p.getName() << std::endl;

    // p.name = "Jiro"; // コンパイルエラー: nameはprivate

    return 0;
}
実行結果
Taro

この例では、外部には「名前を設定する」「名前を取得する」という操作だけを公開し、内部の保存方法(ここではstd::string name)は隠していることがポイントです。

privateとは何か

privateの基本的な意味

privateは「クラスの内部でしか使えない」メンバを宣言するためのアクセス修飾子です。

クラスの外からも、派生クラスの中からも直接アクセスできません。

もっともよく使われるのは、クラスが管理するデータ(メンバ変数)を隠すためです。

これにより、クラスの利用者が間違って内部の状態を壊してしまうことを防ぎます。

privateの役割: データを守る

クラスの内部実装は、将来的に変更されることがあります。

しかし、外部から直接その内部データに依存していると、仕様変更のたびに利用側のコードもすべて書き換える必要が出てしまいます。

そこで、内部のデータはprivateで隠し、publicメソッドだけを約束事(インターフェース)として公開しておけば、内部実装を変更しても、publicの仕様さえ守れば外部コードはそのまま動き続けます。

privateの基本コード例

C++
#include <iostream>

class Counter {
public:
    // カウンタを初期化するコンストラクタ
    Counter(int start) {
        value = start;
    }

    // カウンタを1増やす
    void increment() {
        ++value;
    }

    // 現在値を取得する
    int getValue() const {
        return value;
    }

private:
    // 外部から直接いじらせたくない内部状態
    int value;
};

int main() {
    Counter c(0);

    c.increment();
    c.increment();

    std::cout << "count = " << c.getValue() << std::endl;

    // c.value = 100; // コンパイルエラー: valueはprivate

    return 0;
}
実行結果
count = 2

Counterクラスの利用者は、valueという名前やint型であることを意識する必要がなく、公開されたメソッドだけを使えばよいことがわかります。

protectedとは何か

protectedの基本的な意味

protectedは「クラス自身」と「その派生クラス」からはアクセスできるが、それ以外からはアクセスできないメンバを宣言します。

つまり、継承関係にあるクラス同士だけで共有したい内部情報を定義するときに使います。

protectedのイメージ

protectedは、「外部にまでは見せたくないが、子クラスには使わせたい」という状況で有効です。

大規模なクラス階層や、フレームワークの基底クラスなどでよく利用されます。

protectedの基本コード例

C++
#include <iostream>
#include <string>

class Animal {
public:
    void speak() const {
        // protectedなnameにアクセス可能(同じクラス内)
        std::cout << "I am " << name << std::endl;
    }

protected:
    // 派生クラスにも教えておきたい情報
    std::string name;
};

class Dog : public Animal {
public:
    Dog(const std::string& n) {
        // 派生クラスからprotectedメンバにアクセス可能
        name = n;
    }

    void bark() const {
        // ここでもprotectedなnameにアクセス可能
        std::cout << name << " says: Bow-wow!" << std::endl;
    }
};

int main() {
    Dog d("Pochi");

    d.speak(); // Animalのpublicメソッド
    d.bark();  // Dogのpublicメソッド

    // d.name = "Tama"; // コンパイルエラー: nameはprotectedなので外部からはアクセス不可

    return 0;
}
実行結果
I am Pochi
Pochi says: Bow-wow!

この例では、Animalクラスとその派生クラス(Dog)だけがnameメンバに直接アクセスでき、main関数などの外部コードからはアクセスできないことが確認できます。

public / private / protected の違いを整理

アクセス範囲の比較表

3つの修飾子の違いを、アクセスできる範囲ごとに表にまとめます。

アクセス元publicprotectedprivate
同じクラスのメンバ関数アクセス可アクセス可アクセス可
派生クラスのメンバ関数アクセス可アクセス可アクセス不可
クラス外(通常の関数など)アクセス可アクセス不可アクセス不可

publicだけが「クラスの外」から見えるprotectedは「継承関係の中」だけで共有privateは「そのクラスの中だけ」で利用、という関係になっています。

具体例で3つの違いを確認する

1つのクラスに3種類をまとめて定義

C++
#include <iostream>

class Sample {
public:
    int publicValue; // どこからでも触れる

protected:
    int protectedValue; // 派生クラスからは触れる

private:
    int privateValue; // Sampleの内部だけで使用

public:
    // 3つのメンバにまとめて値を設定するメソッド
    void setValues(int a, int b, int c) {
        publicValue    = a; // 自分のクラスなのでアクセス可
        protectedValue = b; // 自分のクラスなのでアクセス可
        privateValue   = c; // 自分のクラスなのでアクセス可
    }

    // 3つの値を出力するメソッド
    void printValues() const {
        std::cout << "publicValue    = " << publicValue    << std::endl;
        std::cout << "protectedValue = " << protectedValue << std::endl;
        std::cout << "privateValue   = " << privateValue   << std::endl;
    }
};

class Derived : public Sample {
public:
    void accessTest() {
        publicValue    = 10; // OK: public
        protectedValue = 20; // OK: protected
        // privateValue = 30; // NG: privateにはアクセスできない
    }
};

int main() {
    Sample s;
    s.setValues(1, 2, 3);

    // クラス外からアクセス可能なのはpublicだけ
    s.publicValue = 100;
    // s.protectedValue = 200; // NG
    // s.privateValue   = 300; // NG

    s.printValues();

    Derived d;
    d.accessTest();
    d.printValues();

    return 0;
}
実行結果
publicValue    = 100
protectedValue = 2
privateValue   = 3
publicValue    = 10
protectedValue = 20
privateValue   = 3

出力結果を見ると、DerivedクラスはprotectedValueまでは変更できていますが、privateValueは変更できていないことがわかります。

これはDerivedがSampleのprivateメンバにアクセスできないためです。

アクセス修飾子のデフォルトと書き方のポイント

クラスと構造体でのデフォルトの違い

C++では、クラスと構造体でアクセス修飾子のデフォルト値が異なります。

宣言の種類明示しない場合のデフォルトよく使われる用途のイメージ
classprivateメンバを隠蔽するクラス設計
structpublic単純なデータのまとめ(レコード)
C++
class A {
    int x; // 何も書かないとprivateになる
};

struct B {
    int x; // 何も書かないとpublicになる
};

「きちんとカプセル化されたクラス」を作る場合はclassを使い、「単なるデータの束」を表現したいときはstructを使う、というスタイルがよく見られます。

アクセス修飾子はブロックの境界線

アクセス修飾子は、それ以降のメンバすべてに対して有効です。

何度でも切り替えることができます。

C++
class Example {
public:
    void foo();  // public

private:
    int x;       // private

public:
    void bar();  // 再びpublic
};

このように、public / private / protected のラベルでクラスの中を区切るイメージで考えるとわかりやすくなります。

どう使い分ければよいか

基本方針

初学者の方は、まず次のルールから始めるとよいです。

  1. メンバ変数は原則privateにする
  2. 外部から呼んでほしいメソッドだけpublicにする
  3. 継承が必要になったときに、必要なものだけprotectedを検討する

この方針を守るだけでも、クラスの内部状態が守られ、予期しないバグの発生を減らすことができます。

publicにしすぎることの問題

何でもかんでもpublicにしてしまうと、

  • クラス内部の仕様変更が難しくなる
  • 外部コードが内部実装に依存してしまう
  • 不正な状態に変更されやすくなる

といった問題が生じます。

「本当に外から触らせてよいのか」を意識して、必要最小限だけpublicにすることが大切です。

まとめ

public / private / protectedは、C++のクラス設計において「どこまで見せるか・どこまで隠すか」を決めるための基本ルールです。

publicは外部に公開するインターフェース、privateはクラス内部だけで守るデータや処理、protectedは継承関係の中で共有したい要素に使います。

特に、メンバ変数をprivateにしてpublicメソッドだけを通じて操作させるカプセル化の考え方は、堅牢なプログラムを書くうえで重要です。

まずは、小さなクラスでもアクセス修飾子を意識して設計する習慣を身につけるところから始めてみてください。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!