[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++では、クラスのメンバに対するアクセス制御を行うために、publicprivateprotectedの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同じクラスおよび派生クラスからアクセス可能派生クラスに特定のメンバを利用させたい場合

この表からもわかるように、protectedpublicprivateの中間的な位置付けにあり、特に継承を利用した設計において非常に有用です。

クラス設計の目的に応じて、適切なアクセス指定子を選択することが重要です。

実装例

ここでは、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クラスprotectedFunctionDerivedクラスから呼び出されています。

複数の派生クラスでの利用

次に、複数の派生クラスが同じ基底クラスを継承し、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関数が呼ばれました。

この例では、DerivedADerivedBの両方が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を利用することで、基底クラスの機能を派生クラスに引き継ぎつつ、外部からのアクセスを制限することができます。

大規模プロジェクトでは、こうした設計がコードの可読性や保守性を高めるのに役立ちます。

よくある質問

protectedとprivateの違いは何ですか?

protectedprivateは、C++におけるアクセス指定子ですが、アクセス範囲に違いがあります。

  • protected: 同じクラス内およびその派生クラスからアクセス可能です。

これにより、派生クラスは基底クラスのprotectedメンバにアクセスでき、継承を利用した設計が可能になります。

  • private: 同じクラス内からのみアクセス可能で、外部や派生クラスからはアクセスできません。

これにより、クラスの内部実装を完全に隠蔽することができます。

このため、protectedは派生クラスに対して特定の機能を提供したい場合に使用し、privateは外部からのアクセスを完全に制限したい場合に使用します。

protected関数はどのような場面で使うべきですか?

protected関数は、以下のような場面で使用することが推奨されます。

  • 継承を利用する場合: 基底クラスの機能を派生クラスに引き継ぎたい場合、protected関数を使用することで、派生クラスからその機能を利用できるようにします。
  • 内部実装の隠蔽: 基底クラスの内部実装を隠蔽しつつ、派生クラスに必要な機能を提供したい場合に適しています。

これにより、クラスの設計がより安全になります。

  • クラスの拡張性を高める: 将来的に新しい派生クラスを追加する際に、基底クラスのprotectedメンバを利用することで、コードの再利用が促進されます。

派生クラスからのアクセスを制限する方法はありますか?

派生クラスからのアクセスを制限する方法はいくつかあります。

  1. privateメンバの使用: 基底クラスのメンバをprivateとして宣言することで、派生クラスからのアクセスを完全に制限できます。

ただし、これにより派生クラスはそのメンバにアクセスできなくなります。

  1. protectedメンバの利用: protectedメンバを使用する場合でも、基底クラス内で条件を設けて、特定の状況下でのみ派生クラスからアクセスできるようにすることが可能です。

例えば、基底クラス内で特定のフラグを設定し、そのフラグが有効な場合のみprotectedメンバにアクセスできるようにすることが考えられます。

  1. 友達クラスの利用: friendキーワードを使用して、特定のクラスや関数に対してアクセスを許可することができます。

これにより、特定のクラスからのみアクセスを許可し、他のクラスからのアクセスを制限することができます。

これらの方法を組み合わせることで、派生クラスからのアクセスを適切に制御することができます。

まとめ

この記事では、C++におけるprotectedアクセス指定子の使い方やその利点、制限について詳しく解説しました。

また、protectedを利用したクラス設計の実装例や応用例を通じて、継承やアクセス制御の重要性を強調しました。

これを機に、クラス設計においてprotectedを効果的に活用し、より安全で柔軟なプログラムを作成してみてください。

当サイトはリンクフリーです。出典元を明記していただければ、ご自由に引用していただいて構いません。

関連カテゴリーから探す

  • URLをコピーしました!
目次から探す