Cpp クラス 継承 新しいページはコチラ
提供: yonewiki
(→クラス 継承) |
|||
5行: | 5行: | ||
<tr> | <tr> | ||
<td style="width:1px;"></td> | <td style="width:1px;"></td> | ||
− | <td class="mbox-text plainlist" style="">本来の表記は「<b><span id="RealTitle" style="font-size:large;">C++ クラス 継承</span></b>」です。この記事に付けられた題名は{{記事名の制約}}から不正確なものとなっています。</td> | + | <td class="mbox-text plainlist" style="">本来の表記は「<b><span id="RealTitle" style="font-size:large;">C++(Cpp) クラス 継承</span></b>」です。この記事に付けられた題名は{{記事名の制約}}から不正確なものとなっています。</td> |
</tr> | </tr> | ||
</table> | </table> | ||
14行: | 14行: | ||
− | + | 初めて継承という言葉を聞く人はそれって便利なの?って思ったかもしれない。なぜなら、若かりし頃、自分もそれって便利なのみたいな感じでした。継承の技術だけを教えてもらったからなのかもしれない。具体的な活用例って大事なんだなと思うところです。だがしかし!具体的な利用例を考えると異常にながくなるのがサンプルプログラム。継承の説明をしているので、サンプルプログラム全体を見通す必要があり、バランスが大事。カンタンなサンプルで且つ、継承が有意義なプログラムに感じる説明が必要になるのです。でも有意義なプログラムって難しくなり勝ちだから、やっぱ、簡便なものを最初は紹介したいと思います。つまり、サンプルは技術を知るためだけの悪い使い方です。有意義な使い方についてはかなり先になってから、もう一度考えようかなって思ってます。 | |
30行: | 30行: | ||
class CBaseInheritance { | class CBaseInheritance { | ||
protected: | protected: | ||
− | int m_iBaseMoney; | + | int m_iBaseMoney = 0; |
− | int m_iBaseMonth; | + | int m_iBaseMonth = 0; |
int m_fiBaseSumMoney(); | int m_fiBaseSumMoney(); | ||
public: | public: | ||
108行: | 108行: | ||
class CDeriveInheritance :public CBaseInheritance { | class CDeriveInheritance :public CBaseInheritance { | ||
private: | private: | ||
− | int m_iOptionMoney; | + | int m_iOptionMoney = 0; |
− | int m_iDeriveMoney; | + | int m_iDeriveMoney = 0; |
public: | public: | ||
CDeriveInheritance(int iArgDeriveMoney, int iArgDeriveMonth, int iArgOptionMoney); | CDeriveInheritance(int iArgDeriveMoney, int iArgDeriveMonth, int iArgOptionMoney); | ||
132行: | 132行: | ||
メインプログラムのファイル名は何でも良いので、InheritanceMain.cppという名前のファイルにmain関数を置きました。main関数はなんでmainという名前なの?とかwmainって何とか、試しに一度Windowsアプリのプロジェクトを作ったら自動で作られたプログラムのmain関数にあたる部分がなぜかWinMainという名前になってmainという名前が使えなかったことがあるという人は[[VC PlusPlus:サブシステムとエントリポイント]]にmain関数の名前を変えることができるという内容の文献があります。main.cppという名前だったり、プロジェクト名と同じだったり、好きなファイル名にするといいと思います。Consoleアプリプロジェクトの規定値としてはVisualStudioC++のコンパイラはプロジェクトに置かれたプログラムファイル群からmainという名前の関数を探して、探し出したmain関数から始まるプログラムとして、解釈してくれます。リンク先の記事のように名前を変えることも出来るので、気に入らない人は、替えてみるのもよいですね。 | メインプログラムのファイル名は何でも良いので、InheritanceMain.cppという名前のファイルにmain関数を置きました。main関数はなんでmainという名前なの?とかwmainって何とか、試しに一度Windowsアプリのプロジェクトを作ったら自動で作られたプログラムのmain関数にあたる部分がなぜかWinMainという名前になってmainという名前が使えなかったことがあるという人は[[VC PlusPlus:サブシステムとエントリポイント]]にmain関数の名前を変えることができるという内容の文献があります。main.cppという名前だったり、プロジェクト名と同じだったり、好きなファイル名にするといいと思います。Consoleアプリプロジェクトの規定値としてはVisualStudioC++のコンパイラはプロジェクトに置かれたプログラムファイル群からmainという名前の関数を探して、探し出したmain関数から始まるプログラムとして、解釈してくれます。リンク先の記事のように名前を変えることも出来るので、気に入らない人は、替えてみるのもよいですね。 | ||
− | |||
− | |||
− | |||
+ | =メンバ変数の利用用途= | ||
+ | *'''m_iOptionMoney''':オプションで追加購入したものの金額を覚えるものです。最初のコンストラクタで3番目の引数にあたるところの、1つ分のオプション価格であるint型の値しか受け取らないので、一つしかオプションは買えないっす。 | ||
+ | *'''m_iDeriveMoney''': オプションも含めた合計金額を覚えます。基底の基本料金+利用月の合計に上乗せして計算する新しい機能の変数って感じはします。 | ||
+ | |||
+ | |||
+ | =メンバ関数の利用用途= | ||
+ | *'''m_fvDeriveDispValue()''':派生した方法での合計金額の中身を知るための関数 | ||
+ | *'''m_fiDeriveSumMoney()''': 合計金額を取り出す関数 | ||
+ | |||
+ | という感じです。 | ||
+ | そうすると、派生したクラスのプログラム部分のファイルの中身は以下のようになります。 | ||
+ | |||
+ | <span style="color: #ffffff; background-color: #555555; padding: 0px 5px 0px 5px; display: inline-block;">cpp <span>(</span>DeriveInheritance.cpp<span>)</span><!-- padding 上 右 下 左--> | ||
+ | <syntaxhighlight2 lang="cpp" line start=600> | ||
+ | #include <cstdio> | ||
+ | #include "DeriveInheritance.h" | ||
+ | |||
+ | CDeriveInheritance::CDeriveInheritance(int iArgDeriveMoney, int iArgDeriveMonth, int iArgOptionMoney) { | ||
+ | printf("Constructer:CDeriveInheritance(int,int,int)\n"); | ||
+ | m_iBaseMoney = iArgDeriveMoney; | ||
+ | m_iBaseMonth = iArgDeriveMonth; | ||
+ | m_iOptionMoney = iArgOptionMoney; | ||
+ | } | ||
+ | |||
+ | CDeriveInheritance::~CDeriveInheritance() { | ||
+ | printf("Destructer:~CDeriveInheritance()\n"); | ||
+ | } | ||
+ | |||
+ | void CDeriveInheritance::m_fvDeriveDispValue() { | ||
+ | int iSumMoney = m_fiDeriveSumMoney(); | ||
+ | printf("DeriveMoney=%d\n", iSumMoney); | ||
+ | |||
+ | } | ||
+ | |||
+ | int CDeriveInheritance::m_fiDeriveSumMoney() { | ||
+ | return m_fiBaseSumMoney() + m_iOptionMoney; | ||
+ | } | ||
+ | </syntaxhighlight2> | ||
+ | |||
+ | |||
+ | 派生クラスからは基底クラスのアクセス指定子private以外の変数や関数が呼び出せます。基底クラスにどんな変数や関数があるのか知ってないと使えないので、基底クラスがどういう変数や関数を提供しているか、その働きはどうなっているかは、よく知っておく必要があります。基底クラスを知らずに、基底クラスの変数m_iBaseMoneyとか、m_iBaseMonthとかを操ることはできないです。ちなみに、今、あげた2つの変数はアクセス指定子がprotectedになっているので、派生したクラスなら呼び出していいことになっています。3つの引数をうけとる派生クラスのコンストラクタCDeriveInheritance::CDeriveInheritanceは2つを基底クラスのメンバ変数で覚えさせて、1つを派生クラスの変数に覚えさせています。 | ||
+ | |||
+ | |||
+ | CDeriveInheritance::m_fiDeriveSumMoney()関数は基底クラスのメンバ関数から合計金額の値を取得しつつ、派生クラスが覚えている金額と足し合わせている感じです。 | ||
+ | |||
+ | |||
+ | あとは、派生クラスのデストラクタCDeriveInheritance::~CDeriveInheritanceだったり、変数の中身をお披露目するCDeriveInheritance::m_fvDeriveDispValue()は、特に凝ったことはしていないので、説明しません。 | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | このような派生クラスをメインプログラムから、以下のように利用します。 | ||
+ | |||
+ | |||
+ | <span style="color: #ffffff; background-color: #555555; padding: 0px 5px 0px 5px; display: inline-block;">cpp <span>(</span>InheritanceMain.cpp<span>)</span><!-- padding 上 右 下 左--> | ||
+ | <syntaxhighlight2 lang="cpp" line start=600> | ||
+ | #include <cstdio> | ||
+ | #include "DeriveInheritance.h" | ||
+ | |||
+ | int main() { | ||
+ | |||
+ | CDeriveInheritance objCDeriveInheritance(3900, 12, 600); | ||
+ | objCDeriveInheritance.m_fvDeriveDispValue(); | ||
+ | |||
+ | return 0; | ||
+ | } | ||
+ | </syntaxhighlight2> | ||
+ | |||
+ | |||
+ | とすると、実行結果は | ||
+ | |||
+ | <span style="color: #ffffff; background-color: #555555; padding: 0px 5px 0px 5px; display: inline-block;">処理結果 </span><!-- padding 上 右 下 左--> | ||
+ | <syntaxhighlight2 lang="text" line start = 700> | ||
+ | Constructor:CBaseInheritance() | ||
+ | Constructer:CDeriveInheritance(int,int,int) | ||
+ | DeriveMoney = 47400 | ||
+ | Destructer:~CDeriveInheritance() | ||
+ | Destructor:~CBaseInheritance() | ||
+ | </syntaxhighlight2> | ||
+ | |||
+ | |||
+ | のようになります。派生クラスを作成すると、基底のコンストラクタ引数無しの後に派生のコンストラクタ引数(int, int, int)が呼ばれて、メイン関数から、派生クラスの中身を表示させる命令を実行したあと、メイン関数がおわろうとする手前で、派生クラスのデストラクタが呼ばれて、基底クラスのデストラクタが呼ばれて終わっていることがわかります。 | ||
+ | |||
+ | |||
+ | 派生クラスを生成させたときに派生クラス側で引数がある場合、派生クラスはもちろん自身のコンストラクタが呼ばれますが、基底クラスでは、何もないコンストラクタが呼ばれます。基底クラスで、引数付きのコンストラクタが存在するクラスを作っていた場合で派生クラスで初期化がされない事がある場合、引数のないコンストラクタを明記しないとダメです。最初から基底クラスにコンストラクタが存在しないなら、引数のないコンストラクタを明記しておく必要はないです。基底クラスが引数付きコンストラクタで初期化処理するクラスで、派生クラスから呼ばれても影響が無いことを証明するようなものです。あるいは影響があっても派生クラスがちゃんとやってくれるんなら許すという感じでもあります。基底クラスになるにしても、基底クラスになる準備が出来ているか?というものは、求められていることになります。今回の場合は、呼び出されたあとに、値が設定されたかが重要になるので、基底のクラスのコンストラクタで、値を受け取って、基底のクラスが使える状態になったか?というのは基底のコンストラクタで記述しないとダメなのかもしれませんが、勉強用なので、特に何もしませんでした。ほったらかしです。 | ||
+ | |||
+ | |||
+ | 今回のサンプルのように、継承という手法は基底側と派生側とが完璧な関係でないパターンは良くない継承の使い方にも見えます。継承の機能は使い方が大事ということは、なんとなくわかったかなぁと思います。もっといい継承の使い方は、全てを理解したあとで、考えましょう。プログラムに正解はないので、よりよいと思われるとか、あまりよくないとか、本来の使い方とは違うとかいろいろあるのかもしれませんが、目的は達成してしまうことがあるのがプログラムです。後から、機能を追加したり、見直したりするときにわかりやすいとか、いろいろな要素があるので、完璧というのは、難しいです。言語仕様がわかっていて、結果もでていて、まぁメンテナンスもできるし、いいんじゃないかなぁというプログラムでも誰かが見れば、いや、これはこういう使い方しない方がよいのでは?と思われるくらい、奥がふかいので、ここでは誰もが納得のいく答えが提供できる気がしないですが、もう少しくらいは、ゆっくり考えてみたいと思います。後で。(先延ばし主義) | ||
+ | |||
+ | |||
+ | メイン関数からは、派生クラスが基底クラスを使っているかどうかは、わからないくらいの状態でプログラムされています。インクルードしているヘッダファイルも基底クラスを直接使わない分には、基底クラス用のヘッダファイルを読み込む必要もないです。基底クラスがすべてのことの根幹をなしていて、重要な機能を成しているほど、それをもとに作った、派生したクラスは基底クラスがあって有り難いはず。そして派生クラスは、基底クラスに機能を追加して、充実したものになる。そういう関係が大事だと思います。 | ||
− | |||
[[C PlusPlus#C++からの技術|C++]]に戻る | [[C PlusPlus#C++からの技術|C++]]に戻る |