[C++] 派生クラスからのみ基底クラスの関数を呼べるようにする方法(protected)
C++で基底クラスの関数を派生クラスからのみ呼び出せるようにするには、その関数をprotected
アクセス指定子の下に定義します。
protected
に設定されたメンバーは、同じクラス内およびその派生クラス内からアクセス可能ですが、クラス外部や非派生クラスからはアクセスできません。
これにより、基底クラスの関数を派生クラス専用にすることができます。
例えば、基底クラスのメンバー関数をprotected
にすることで、派生クラスはその関数を自由に利用できる一方で、外部からの直接アクセスを防ぐことができます。
- protectedアクセス指定子の特徴
- 基底クラスと派生クラスの関係
- 継承を利用した設計パターン
- アクセス制御の重要性
- 大規模プロジェクトでの活用方法
派生クラスからのみ基底クラスの関数を呼び出す方法
基底クラスでのprotected関数の定義
C++では、基底クラスにおいてprotected
アクセス指定子を使用することで、派生クラスからのみアクセス可能なメンバ関数を定義できます。
以下は、基底クラスにprotected関数
を定義する例です。
#include <iostream>
using namespace std;
class Base {
protected:
void protectedFunction() {
cout << "基底クラスのprotected関数が呼ばれました。" << endl;
}
};
このコードでは、Baseクラス
にprotectedFunction
という関数が定義されています。
この関数はprotected
として宣言されているため、Baseクラス
の外部からは直接呼び出すことができません。
派生クラスでのprotected関数の利用
派生クラスでは、基底クラスのprotected関数
にアクセスすることができます。
以下の例では、Derivedクラス
がBaseクラス
を継承し、protectedFunction
を呼び出しています。
#include <iostream>
using namespace std;
class Base {
protected:
void protectedFunction() {
cout << "基底クラスのprotected関数が呼ばれました。" << endl;
}
};
class Derived : public Base {
public:
void callProtectedFunction() {
protectedFunction(); // 派生クラスから基底クラスのprotected関数を呼び出す
}
};
int main() {
Derived derived;
derived.callProtectedFunction(); // 派生クラスの関数を通じて呼び出す
return 0;
}
基底クラスのprotected関数が呼ばれました。
この例では、Derivedクラス
のcallProtectedFunctionメソッド
を通じて、基底クラスのprotectedFunction
が呼び出されています。
外部からのアクセス制限
protected
アクセス指定子を使用することで、基底クラスのメンバ関数は派生クラスからのみアクセス可能となり、外部からのアクセスを制限できます。
以下の例では、外部からprotectedFunction
を呼び出そうとすると、コンパイルエラーが発生します。
#include <iostream>
using namespace std;
class Base {
protected:
void protectedFunction() {
cout << "基底クラスのprotected関数が呼ばれました。" << endl;
}
};
int main() {
Base base;
// base.protectedFunction(); // これはコンパイルエラーになります
return 0;
}
このように、protected関数
は派生クラスからのみ呼び出すことができ、外部からのアクセスを防ぐことができます。
これにより、クラスの設計においてカプセル化を強化することが可能です。
アクセス指定子の種類
C++では、クラスのメンバに対するアクセス制御を行うために、public
、private
、protected
の3つのアクセス指定子が用意されています。
それぞれの特徴について詳しく見ていきましょう。
publicの特徴
- アクセス範囲:
public
で宣言されたメンバは、クラスの外部からもアクセス可能です。 - 利用シーン: 他のクラスや関数から自由に利用されるべきメンバに使用します。
- 例: クラスのインターフェースやAPIとして公開するメソッドや変数に適しています。
#include <iostream>
using namespace std;
class PublicExample {
public:
void publicFunction() {
cout << "これはpublic関数です。" << endl;
}
};
int main() {
PublicExample example;
example.publicFunction(); // 外部からアクセス可能
return 0;
}
これはpublic関数です。
privateの特徴
- アクセス範囲:
private
で宣言されたメンバは、同じクラス内からのみアクセス可能で、外部からはアクセスできません。 - 利用シーン: クラスの内部実装を隠蔽し、外部からの不正なアクセスを防ぐために使用します。
- 例: 内部状態を保持する変数や、外部に公開する必要のない補助メソッドに適しています。
#include <iostream>
using namespace std;
class PrivateExample {
private:
void privateFunction() {
cout << "これはprivate関数です。" << endl;
}
public:
void callPrivateFunction() {
privateFunction(); // 同じクラス内からはアクセス可能
}
};
int main() {
PrivateExample example;
// example.privateFunction(); // これはコンパイルエラーになります
example.callPrivateFunction(); // publicメソッドを通じて呼び出す
return 0;
}
これはprivate関数です。
protectedの特徴
- アクセス範囲:
protected
で宣言されたメンバは、同じクラス内およびその派生クラスからアクセス可能ですが、外部からはアクセスできません。 - 利用シーン: 派生クラスに対して特定のメンバを利用させたい場合に使用します。
- 例: 基底クラスの機能を派生クラスに引き継ぎたいが、外部からのアクセスを制限したい場合に適しています。
#include <iostream>
using namespace std;
class ProtectedExample {
protected:
void protectedFunction() {
cout << "これはprotected関数です。" << endl;
}
};
class Derived : public ProtectedExample {
public:
void callProtectedFunction() {
protectedFunction(); // 派生クラスからアクセス可能
}
};
int main() {
Derived derived;
derived.callProtectedFunction(); // 派生クラスの関数を通じて呼び出す
// ProtectedExample example;
// example.protectedFunction(); // これはコンパイルエラーになります
return 0;
}
これはprotected関数です。
これらのアクセス指定子を適切に使い分けることで、クラスの設計をより安全で柔軟にすることができます。
protectedアクセス指定子の詳細
protected
アクセス指定子は、C++におけるクラス設計において重要な役割を果たします。
ここでは、protected
の利点、制限、そして他のアクセス指定子との比較について詳しく解説します。
protectedの利点
- カプセル化の強化:
protected
を使用することで、基底クラスの内部実装を隠蔽しつつ、派生クラスに対して必要な機能を提供できます。
これにより、クラスの設計がより安全になります。
- 継承の活用: 派生クラスが基底クラスの
protected
メンバにアクセスできるため、コードの再利用が促進されます。
これにより、共通の機能を持つクラス群を簡単に作成できます。
- 柔軟な設計:
protected
メンバを使用することで、将来的にクラスの拡張が容易になります。
新しい派生クラスを追加する際に、基底クラスの機能をそのまま利用できます。
protectedの制限
- 外部からのアクセス不可:
protected
メンバは、同じクラスや派生クラスからはアクセス可能ですが、クラスの外部からはアクセスできません。
これにより、外部からの不正な操作を防ぐことができますが、外部からの利用ができないため、柔軟性が制限されることがあります。
- 多重継承の複雑さ: 多重継承を使用する場合、
protected
メンバのアクセス権が複雑になることがあります。
特に、異なる基底クラスから同名のprotected
メンバを持つ場合、どのメンバにアクセスするかが不明瞭になることがあります。
他のアクセス指定子との比較
アクセス指定子 | アクセス範囲 | 利用シーン |
---|---|---|
public | クラス外部からもアクセス可能 | 他のクラスや関数から利用されるべきメンバ |
private | 同じクラス内からのみアクセス可能 | 内部実装を隠蔽し、外部からの不正なアクセスを防ぐ |
protected | 同じクラスおよび派生クラスからアクセス可能 | 派生クラスに特定のメンバを利用させたい場合 |
この表からもわかるように、protected
はpublic
とprivate
の中間的な位置付けにあり、特に継承を利用した設計において非常に有用です。
クラス設計の目的に応じて、適切なアクセス指定子を選択することが重要です。
実装例
ここでは、protected
アクセス指定子を使用した具体的な実装例をいくつか紹介します。
基本的なコード例から、複数の派生クラスでの利用、そしてprotected関数
のオーバーライドまで、段階的に解説します。
基本的なコード例
まずは、protected
メンバ関数を持つ基底クラスと、それを利用する派生クラスの基本的な例を示します。
#include <iostream>
using namespace std;
class Base {
protected:
void protectedFunction() {
cout << "基底クラスのprotected関数が呼ばれました。" << endl;
}
};
class Derived : public Base {
public:
void callProtectedFunction() {
protectedFunction(); // 派生クラスからprotected関数を呼び出す
}
};
int main() {
Derived derived;
derived.callProtectedFunction(); // 派生クラスの関数を通じて呼び出す
return 0;
}
基底クラスのprotected関数が呼ばれました。
この例では、Baseクラス
のprotectedFunction
がDerivedクラス
から呼び出されています。
複数の派生クラスでの利用
次に、複数の派生クラスが同じ基底クラスを継承し、protected
メンバ関数を利用する例を示します。
#include <iostream>
using namespace std;
class Base {
protected:
void protectedFunction() {
cout << "基底クラスのprotected関数が呼ばれました。" << endl;
}
};
class DerivedA : public Base {
public:
void callProtectedFunction() {
cout << "DerivedAからの呼び出し: ";
protectedFunction(); // 派生クラスAから呼び出す
}
};
class DerivedB : public Base {
public:
void callProtectedFunction() {
cout << "DerivedBからの呼び出し: ";
protectedFunction(); // 派生クラスBから呼び出す
}
};
int main() {
DerivedA derivedA;
derivedA.callProtectedFunction(); // DerivedAの関数を通じて呼び出す
DerivedB derivedB;
derivedB.callProtectedFunction(); // DerivedBの関数を通じて呼び出す
return 0;
}
DerivedAからの呼び出し: 基底クラスのprotected関数が呼ばれました。
DerivedBからの呼び出し: 基底クラスのprotected関数が呼ばれました。
この例では、DerivedA
とDerivedB
の両方がBaseクラス
のprotectedFunction
を利用しています。
protected関数のオーバーライド
最後に、protected
メンバ関数をオーバーライドする例を示します。
派生クラスで基底クラスのprotected関数
を上書きすることができます。
#include <iostream>
using namespace std;
class Base {
protected:
void protectedFunction() {
cout << "基底クラスのprotected関数が呼ばれました。" << endl;
}
};
class Derived : public Base {
protected:
void protectedFunction() override { // オーバーライド
cout << "派生クラスのprotected関数が呼ばれました。" << endl;
}
public:
void callProtectedFunction() {
protectedFunction(); // 派生クラスのprotected関数を呼び出す
}
};
int main() {
Derived derived;
derived.callProtectedFunction(); // 派生クラスの関数を通じて呼び出す
return 0;
}
派生クラスのprotected関数が呼ばれました。
この例では、Derivedクラス
がBaseクラス
のprotectedFunction
をオーバーライドし、異なる動作を実装しています。
これにより、派生クラスは基底クラスの機能を拡張することができます。
応用例
protected
アクセス指定子は、C++のクラス設計において非常に有用です。
ここでは、継承を利用した設計パターン、protected
を用いたアクセス制御、大規模プロジェクトでの活用について具体的な例を挙げて解説します。
継承を利用した設計パターン
継承を利用した設計パターンの一例として、Template Method Patternがあります。
このパターンでは、基底クラスに共通の処理を定義し、派生クラスで特定の処理を実装します。
protected
メンバを使用することで、基底クラスの処理を派生クラスから利用できます。
#include <iostream>
using namespace std;
class AbstractClass {
protected:
void templateMethod() {
cout << "共通処理を実行中..." << endl;
specificMethod(); // 派生クラスの特定処理を呼び出す
}
virtual void specificMethod() = 0; // 派生クラスで実装する純粋仮想関数
};
class ConcreteClassA : public AbstractClass {
protected:
void specificMethod() override {
cout << "ConcreteClassAの特定処理を実行中..." << endl;
}
};
class ConcreteClassB : public AbstractClass {
protected:
void specificMethod() override {
cout << "ConcreteClassBの特定処理を実行中..." << endl;
}
};
int main() {
ConcreteClassA classA;
classA.templateMethod(); // 共通処理と特定処理を実行
ConcreteClassB classB;
classB.templateMethod(); // 共通処理と特定処理を実行
return 0;
}
共通処理を実行中...
ConcreteClassAの特定処理を実行中...
共通処理を実行中...
ConcreteClassBの特定処理を実行中...
このように、protected
メンバを利用することで、共通の処理を持ちながら、派生クラスごとに異なる処理を実装できます。
protectedを用いたアクセス制御
protected
アクセス指定子は、クラスの内部実装を隠蔽しつつ、派生クラスに対して特定の機能を提供するためのアクセス制御に役立ちます。
これにより、クラスの設計がより安全で柔軟になります。
例えば、以下のようにprotected
メンバを使用して、基底クラスの内部状態を派生クラスにのみ公開することができます。
#include <iostream>
using namespace std;
class Base {
protected:
int value; // 派生クラスからアクセス可能な内部状態
public:
Base(int v) : value(v) {}
};
class Derived : public Base {
public:
Derived(int v) : Base(v) {}
void displayValue() {
cout << "内部状態の値: " << value << endl; // 派生クラスからアクセス
}
};
int main() {
Derived derived(42);
derived.displayValue(); // 内部状態を表示
return 0;
}
内部状態の値: 42
このように、protected
を用いることで、外部からの不正なアクセスを防ぎつつ、派生クラスに必要な情報を提供できます。
大規模プロジェクトでの活用
大規模プロジェクトでは、クラスの設計が複雑になることが多く、protected
アクセス指定子を活用することで、クラス間の関係を明確にし、メンテナンス性を向上させることができます。
例えば、ゲーム開発において、キャラクターの基底クラスを作成し、protected
メンバを使用して共通の機能を持たせることができます。
#include <iostream>
using namespace std;
class Character {
protected:
int health;
int attackPower;
public:
Character(int h, int a) : health(h), attackPower(a) {}
void takeDamage(int damage) {
health -= damage;
cout << "ダメージを受けた!残りの健康: " << health << endl;
}
};
class Warrior : public Character {
public:
Warrior(int h, int a) : Character(h, a) {}
void attack(Character& target) {
cout << "Warriorが攻撃!" << endl;
target.takeDamage(attackPower);
}
};
int main() {
Warrior warrior(100, 20);
Character enemy(80, 10);
warrior.attack(enemy); // Warriorが敵を攻撃
return 0;
}
Warriorが攻撃!
ダメージを受けた!残りの健康: 60
このように、protected
を利用することで、基底クラスの機能を派生クラスに引き継ぎつつ、外部からのアクセスを制限することができます。
大規模プロジェクトでは、こうした設計がコードの可読性や保守性を高めるのに役立ちます。
よくある質問
まとめ
この記事では、C++におけるprotected
アクセス指定子の使い方やその利点、制限について詳しく解説しました。
また、protected
を利用したクラス設計の実装例や応用例を通じて、継承やアクセス制御の重要性を強調しました。
これを機に、クラス設計においてprotected
を効果的に活用し、より安全で柔軟なプログラムを作成してみてください。