継承のアクセスレベル
CoddyのC++ジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 49/104。
選択する継承タイプは、派生クラスで基底クラスのメンバにどのようにアクセスするかに影響します。C++には、public、protected、およびprivate継承の3つのオプションがあります。
各継承タイプがメンバアクセスをどのように変換するかは以下の通りです:
| 基底メンバ | public継承 | protected継承 | private継承 |
|---|---|---|---|
| public | public | protected | private |
| protected | protected | protected | private |
| private | アクセス不可 | アクセス不可 | アクセス不可 |
class Base {
public:
int pub;
protected:
int prot;
private:
int priv;
};
class PublicDerived : public Base {
// pub は public、prot は protected、priv はアクセス不可
};
class ProtectedDerived : protected Base {
// pub は protected、prot は protected、priv はアクセス不可
};
class PrivateDerived : private Base {
// pub は private、prot は private、priv はアクセス不可
};公開継承は「is-a」関係をモデル化するもので、断然、最も一般的です。DogはAnimalであるため、外部コードはそれを1つのものとして扱うことができます。
プロテクト継承とプライベート継承は、「〜を使って実装されている(implemented-in-terms-of)」という関係をモデル化します。これらは基底クラスのインターフェースを外部のコードから隠すため、継承関係を公開せずに実装を再利用したい場合に便利です。プライベート継承では、さらに派生したクラスであっても、元の基底クラスのメンバへのアクセス権を失います。
チャレンジ
簡単シンプルなデバイスの階層構造を構築することで、異なる継承タイプがメンバーのアクセシビリティにどのように影響するかを調べてみましょう。ベースとなる Device クラスと、それぞれ異なる継承タイプを使用する3つの派生クラスを作成し、public、protected、private 継承がメンバーアクセスをどのように変化させるかを直接確認します。
コードは2つのファイルに分けて構成します:
Device.h: 異なるアクセスレベルを持つ3つのメンバーを含むベースクラスDeviceを定義します:- public な
std::string model - protected な
int serialNumber - private な
int internalCode(デフォルトで 999 に設定) Model: <model>と出力する public メソッドshowModel()
次に、それぞれ異なる継承タイプを使用して
Deviceから継承する3つの派生クラスを作成します:- public 継承を使用する
PublicDevice—Serial: <serialNumber>と出力するメソッドdisplaySerial()を追加します - protected 継承を使用する
ProtectedDevice—Serial: <serialNumber>と出力するメソッドdisplaySerial()を追加します - private 継承を使用する
PrivateDevice—Serial: <serialNumber>と出力するメソッドdisplaySerial()を追加します
各派生クラスには、モデル名とシリアル番号を受け取り、モデル名をベースクラスに渡し、シリアル番号を設定するコンストラクタが必要です。
- public な
main.cpp: 2つの入力(それぞれ別の行)を読み取ります:- モデル名 (文字列)
- シリアル番号 (整数)
同じ入力値を使用して、各派生型のオブジェクトを1つずつ作成します。次に、アクセスの違いを示します:
PublicDeviceの場合:- オブジェクトに対して直接
showModel()を呼び出します(public 継承により public のまま維持されるため機能します) modelに直接アクセスし、Direct model access: <model>と出力しますdisplaySerial()を呼び出します
ProtectedDeviceおよびPrivateDeviceの場合:displaySerial()のみを呼び出します(継承されたshowModel()とmodelは、外部から public にアクセスできなくなっています)
出力形式:
--- PublicDevice --- Model: <model> Direct model access: <model> Serial: <serial> --- ProtectedDevice --- Serial: <serial> --- PrivateDevice --- Serial: <serial>
例えば、入力が Laptop と 12345 の場合:
--- PublicDevice ---
Model: Laptop
Direct model access: Laptop
Serial: 12345
--- ProtectedDevice ---
Serial: 12345
--- PrivateDevice ---
Serial: 12345PublicDevice だけが、継承された public メンバーへの外部アクセスを許可していることに注目してください。他の継承タイプは、外部コードからベースクラスのインターフェースを隠しますが、派生クラスは自身のメソッドを通じて内部的に protected メンバーにアクセスし続けることができます。
チートシート
C++には、派生クラスで基底クラスのメンバにどのようにアクセスするかを制御する3つの継承タイプがあります:
| 基底クラスのメンバ | public継承 | protected継承 | private継承 |
|---|---|---|---|
| public | public | protected | private |
| protected | protected | protected | private |
| private | アクセス不可 | アクセス不可 | アクセス不可 |
class Base {
public:
int pub;
protected:
int prot;
private:
int priv;
};
class PublicDerived : public Base {
// pubはpublic、protはprotected、privはアクセス不可
};
class ProtectedDerived : protected Base {
// pubはprotected、protはprotected、privはアクセス不可
};
class PrivateDerived : private Base {
// pubはprivate、protはprivate、privはアクセス不可
};public継承は「is-a」関係をモデル化し、最も一般的です。外部のコードは、派生クラスを通じて基底クラスのpublicメンバにアクセスできます。
protected継承およびprivate継承は「〜を使って実装される(implemented-in-terms-of)」関係をモデル化します。これらは外部のコードから基底クラスのインターフェースを隠します。private継承では、さらにその先の派生クラスも元の基底クラスのメンバへのアクセス権を失います。
自分で試してみよう
#include <iostream>
#include <string>
#include "Device.h"
using namespace std;
int main() {
// 入力を読み込む
string modelName;
int serialNum;
getline(cin, modelName);
cin >> serialNum;
// TODO: 入力値を使用して、各派生型のオブジェクトを作成する
// PublicDevice pubDev(modelName, serialNum);
// ProtectedDevice protDev(modelName, serialNum);
// PrivateDevice privDev(modelName, serialNum);
// TODO: PublicDevice のアクセスを実演する
cout << "--- PublicDevice ---" << endl;
// - オブジェクトに対して直接 showModel() を呼び出す
// - model に直接アクセスして出力する: Direct model access: <model>
// - displaySerial() を呼び出す
// TODO: ProtectedDevice のアクセスを実演する
cout << "--- ProtectedDevice ---" << endl;
// - displaySerial() のみを呼び出す (showModel と model は公開アクセス不可)
// TODO: PrivateDevice のアクセスを実演する
cout << "--- PrivateDevice ---" << endl;
// - displaySerial() のみを呼び出す (showModel と model は公開アクセス不可)
return 0;
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
3コンストラクタとデストラクタ
デフォルトコンストラクタ引数付きコンストラクタコピーコンストラクタムーブコンストラクタコンストラクタ初期化リスト委譲コンストラクタデストラクタ詳解Rule of Three / Five / Zeroまとめ - Stringクラス6演算子オーバーロード
演算子オーバーロード入門算術演算子のオーバーロード比較演算子のオーバーロードストリーム演算子代入演算子のオーバーロード[] と () 演算子のオーバーロード型変換演算子まとめ:Matrix クラス