C++でクラスを学ぶと必ず登場するのが、public / private / protectedという3つのアクセス修飾子です。
どれも短い英単語ですが、オブジェクト指向プログラミングを理解するうえで非常に重要な役割を持っています。
本記事では、初心者の方でもイメージしやすいように、図解と具体的なコード例を用いながら、3つのアクセス修飾子の違いと使い分けを丁寧に説明していきます。
C++のアクセス修飾子とは
アクセス修飾子のイメージ

C++におけるアクセス修飾子とは、クラスのメンバ(変数や関数)を、誰からどこまで見せるかを決めるための「公開レベルの設定」です。
具体的には次の3種類があります。
- public … どこからでもアクセスできる
- private … クラスの内部からしかアクセスできない
- protected … クラス自身と、その派生クラスからはアクセスできるが、その他からはアクセスできない
この3つをうまく使うことで、大事なデータを守りつつ、必要な操作だけを外部に公開することができます。
なぜアクセス修飾子が必要なのか

アクセス修飾子がないと、誰でもクラス内部の変数を書き換えられてしまいます。
例えば、銀行口座の残高を表す変数があったとして、外部から自由に変更できてしまうと、勝手に残高を増やす、といった不正な操作も技術的には可能になってしまいます。
そこでC++では、大事なデータ(フィールド)はprivateで隠し、外部からは安全性を確保したpublicメソッドを通してしか操作させない、という設計が推奨されます。
これが「カプセル化」と呼ばれる考え方です。
publicとは何か
publicの基本的な意味
publicは「誰でも見てよい・使ってよい」メンバを宣言するためのアクセス修飾子です。
クラスの外からも、派生クラスの中からも、同じようにアクセスできます。
クラスの「外に公開するインターフェース」を定義するときに使うのがpublicです。
たとえば「値を設定する」「値を取得する」といったメソッドは、多くの場合publicになります。
publicの基本コード例
#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の基本コード例
#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の基本コード例
#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つの修飾子の違いを、アクセスできる範囲ごとに表にまとめます。
| アクセス元 | public | protected | private |
|---|---|---|---|
| 同じクラスのメンバ関数 | アクセス可 | アクセス可 | アクセス可 |
| 派生クラスのメンバ関数 | アクセス可 | アクセス可 | アクセス不可 |
| クラス外(通常の関数など) | アクセス可 | アクセス不可 | アクセス不可 |
publicだけが「クラスの外」から見える、protectedは「継承関係の中」だけで共有、privateは「そのクラスの中だけ」で利用、という関係になっています。
具体例で3つの違いを確認する
1つのクラスに3種類をまとめて定義
#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++では、クラスと構造体でアクセス修飾子のデフォルト値が異なります。
| 宣言の種類 | 明示しない場合のデフォルト | よく使われる用途のイメージ |
|---|---|---|
| class | private | メンバを隠蔽するクラス設計 |
| struct | public | 単純なデータのまとめ(レコード) |
class A {
int x; // 何も書かないとprivateになる
};
struct B {
int x; // 何も書かないとpublicになる
};
「きちんとカプセル化されたクラス」を作る場合はclassを使い、「単なるデータの束」を表現したいときはstructを使う、というスタイルがよく見られます。
アクセス修飾子はブロックの境界線
アクセス修飾子は、それ以降のメンバすべてに対して有効です。
何度でも切り替えることができます。
class Example {
public:
void foo(); // public
private:
int x; // private
public:
void bar(); // 再びpublic
};
このように、public / private / protected のラベルでクラスの中を区切るイメージで考えるとわかりやすくなります。
どう使い分ければよいか
基本方針
初学者の方は、まず次のルールから始めるとよいです。
- メンバ変数は原則privateにする
- 外部から呼んでほしいメソッドだけpublicにする
- 継承が必要になったときに、必要なものだけprotectedを検討する
この方針を守るだけでも、クラスの内部状態が守られ、予期しないバグの発生を減らすことができます。
publicにしすぎることの問題

何でもかんでもpublicにしてしまうと、
- クラス内部の仕様変更が難しくなる
- 外部コードが内部実装に依存してしまう
- 不正な状態に変更されやすくなる
といった問題が生じます。
「本当に外から触らせてよいのか」を意識して、必要最小限だけpublicにすることが大切です。
まとめ
public / private / protectedは、C++のクラス設計において「どこまで見せるか・どこまで隠すか」を決めるための基本ルールです。
publicは外部に公開するインターフェース、privateはクラス内部だけで守るデータや処理、protectedは継承関係の中で共有したい要素に使います。
特に、メンバ変数をprivateにしてpublicメソッドだけを通じて操作させるカプセル化の考え方は、堅牢なプログラムを書くうえで重要です。
まずは、小さなクラスでもアクセス修飾子を意識して設計する習慣を身につけるところから始めてみてください。
