Version 17.04
アクセス修飾子のまとめ
「前回は protected について説明しました」
『派生クラスからアクセスできるのが protected 、できないのが private 、
全部OKが public ……ややこしー』
「そこで、今回は全部まとめてみます」
『おお』
「まず、メンバが〈どこからアクセスできるか〉を指定する public 、
private 、 protected を【アクセス修飾子】とか【アクセス指定子】とい
います」
『あくせすしゅうしょくし、ね』
「これらを使用することで、メンバ変数やメンバ関数がどこから使えるのか
を指定することができます。表にすると、以下のようになります」
■ public のアクセス範囲
○以下の箇所からアクセスできます。
・あらゆるメンバ関数
・あらゆる関数
○以下の箇所からはアクセスできません。
・アクセスできない箇所はありません
■ protected のアクセス範囲
○以下の箇所からアクセスできます。
・自クラスのメンバ関数
・派生クラスのメンバ関数
○以下の箇所からはアクセスできません。
・派生クラス以外の他クラスのメンバ関数
・通常の関数
■ private のアクセス範囲
○以下の箇所からアクセスできます。
・自クラスのメンバ関数
○以下の箇所からはアクセスできません。
・(派生クラスを含む)他クラスのメンバ関数
・通常の関数
「次は図で見てみます。たとえば、以下のクラスがあるとします」
// CData クラス。
class CData
{
public:
int m_iPublic;
protected:
int m_iProtected;
private:
int m_iPrivate;
};
『……そういえば、アクセス修飾子って、全部【 p 】から始まるんだね』
「だからちょっと紛らわしいかも……それは置いといて、アクセスできる
範囲を図にすると以下のようになります。以下の図は、」
・CData クラスのメンバ関数
・CData クラスの派生クラスのメンバ関数
・まったく関係ないクラスのメンバ関数
・ただの関数
「から、」
CData クラスのメンバ変数
・m_iPublic
・m_iProtected
・m_iPrivate
「にアクセスできるか、という図です。アクセスできたら○、できなかった
ら×です」
┌──────────────┐
│ CData クラスの派生クラス │
│ ┌────────┐ │
│ │CData クラス │ │
│ │ │ │
│ │ ○m_iPublic │ │
│ │ ○m_iProtected │ │
│ │ ○m_iPrivate │ │
│ └────────┘ │
│ ○m_iPublic │
│ ○m_iProtected │
│ ×m_iPrivate │
└──────────────┘
┌──────────────┐
│ まったく関係ないクラス │
│ │
│ ○m_iPublic │
│ ×m_iProtected │
│ ×m_iPrivate │
└──────────────┘
┌──────────────┐
│ ただの関数 │
│ │
│ ○m_iPublic │
│ ×m_iProtected │
│ ×m_iPrivate │
└──────────────┘
『これだと一目瞭然ねー。 public はどこからでも、 private は自クラス
だけ、 protected はその中間、ってわけね』
「そういうこと」
『……で、具体的な使い分けってどうすればいいの? メンバ変数は
private にするとか、メンバ関数は public にするとかはわかるけど』
「そうだね、使い分けは以下のような感じかな」
■メンバ変数
・普通:
→ private
・派生クラスから使えるようにしたい場合:
→ protected
■メンバ関数
・外から呼び出す場合:
→ public
・派生クラスからのみ呼び出せるようにしたい場合:
→ protected
・自クラスでの処理専用:
→ private
『むむむ、質問!』
「はい火美ちゃん」
『派生クラスから使うからメンバ変数を protected にする、っていうのは
イカンと思います!』
「だね、カプセル化の考えと反するからね」
『うんうん』
「これが許されるのは、基本クラスと派生クラスを一緒に作るような場合か
な」
『どゆこと?』
「基本クラスと派生クラスはあくまでペア。他の派生クラスを作ることや、
基本クラス単体で使うことを想定していなくて、基本クラスと派生クラスを
ひとつのクラスと考えるような場合、とか」
『ひとつのクラスだからメンバ変数に直接アクセスできてもいい、と』
「そういうこと。でも気になるのは重要だから、気になる場合にはメンバ変数
は private にしてアクセサ作って」
『それを派生クラスから呼び出せばいいわけね。そのアクセサを protected に
すればいい、と』
「そうすればメンバ変数のカプセル化は守られるからね。そっちの方が推奨
かも」
『んじゃそうする。メンバ関数は……こっちはいいかな』
「ただ、メンバ関数の方について注意点がふたつ。まず、 protected は限
りなく public に近い、と考えてください」
『ええっ? それって違くない? private に、派生クラスから使えるって
いうのが加わったのが protected じゃない』
「そうなんだけど、その許可がとても大きいんです。 private だと、
自クラス以外からは絶対にアクセスできません。たとえ派生クラスでも」
『うん、さっきの m_iPrivate メンバ変数も自クラス以外からは絶対に使え
なかったもんね』
「でも、それが protected になると話が違うんです」
『? 派生クラスからアクセスできるようになったってだけじゃん』
「それがとても大きいんです。前回の SetData() メンバ関数の例を思い出
して」
『んー、基本クラスの m_iData メンバ変数は protected で、派生クラスの
SetData() メンバ関数からはアクセスできた、けど外の WinMain() 関数か
らはアクセスできない、だよね』
「でも WinMain() は SetData() にはアクセスできるでしょ」
『そりゃ public だから……あ』
「つまり、 protected メンバにアクセスできる public なメンバ関数を
派生クラスで作っちゃうと、実質的に protected メンバが public メンバ
になっちゃう、ってことなんです」
┌──────────────┐
│ CDerivedData │
│ ┌────────┐ │
│ │CData │ │
│ │ │ │
│ │m_iData ×←←←←←←←←←← WinMain()
│ │ ○ │ │ ↓
│ │ ↑ │ │ ↓
│ └─↑──────┘ │ ↓
│ ↑ │ ↓
│ SetData()←←←←←←←←←←←←←←
└──────────────┘
「こんな感じに、 WinMain() は直接 m_iData に値をセットできなくても、
SetData() メンバ関数を通せば間接的に値をセットできてしまうんです」
『つまり protected が public に……』
「派生クラスは自由に作れるから、たとえば……」
・CData クラスの m_iData メンバ変数にアクセスできなくて不便だなぁ
↓
・あ、 m_iData メンバ変数は protected だ
↓
・CData クラスの派生クラス作っちゃおう
↓
・そのクラスで m_iData の getter と setter を作っちゃおう
↓
・そうしたら実質的に m_iData が外からアクセスできるように!
『げげげげげ!』
「こういうことができちゃうのが protected 、できないのが private 。
この差はとても大きいってこと」
『だから protected は public に近い、ってわけね……』
/*
Preview Next Story!
*/
『 protected は private 寄りじゃなくて public 似……むつかしい』
「シンタックスとセマンティクスの違いは難しいかもね」
『し、しんた……???』
「次回は分かりやすい CWnd クラスの話」
『あ、分かりやすそう』
「というわけで次回」
< Version 17.05 CWnd クラスの継承関係 >
『につづく!』
「分かりやすいのうれしい! MFC 万歳!!」
『そ、そんなに!?』