Cpp クラス 継承 アップキャストのソースを表示
新しいページはコチラ
移動:
案内
,
検索
[[C PlusPlus#C++からの技術|C++]]に戻る <table class="mbox-small" style="border:1px solid #aaa; background-color:#f9f9f9; width:22em;" id="RealTitleBanner"> <tr> <td style="width:1px;"></td> <td class="mbox-text plainlist" style="">本来の表記は「<b><span id="RealTitle" style="font-size:large;">C++ クラス 継承 アップキャスト</span></b>」です。この記事に付けられた題名は{{記事名の制約}}から不正確なものとなっています。</td> </tr> </table> ※このページでは[[C PlusPlus|C++]]にのみ存在する機能として、記事タイトルが<nowiki>C++ クラス 継承 アップキャスト</nowiki>になっています。<br /> <br /> == '''クラス 継承 アップキャスト''' == 継承は基底クラスの機能を派生クラスが引き継ぐというものでした。この関係を利用して、基底クラスのポインタが派生クラスのポインタを受け取れるというのがアップキャストです。キャストは役を割り当てるという意味で、出演者一覧の役当て表つまりはキャストがドラマのエンドロールに流れるのでもお馴染みの意味なのでしっくりきます。なにがアップなのか?というところでは、引っかかるものがあります。基底クラスではBaseといったり土台というような意味合いがつよく派生は上に乗っかってるクラスのようなイメージがありそうですが、派生クラスは基底クラスにぶら下がっているというイメージなら上へのキャストというのはしっくりくる。そういう意味でアップキャストです。 だから? 派生クラスのポインタ変数が基底クラスのポインタ変数へ受け渡すことが出来るということを意味しています。つまり派生→基底をアップと言っています。受け渡すは役割を割り当てるキャストだと言っています。なんで、そんなことが出来るん?という感じもします。 考えてみると、派生クラスは、基底クラスを引き継いでいるし、派生クラス独自の関数や変数を持っているから、基底クラスよりも機能が多いはずです。そうすると、派生クラスは、少なくとも基底クラスは含んでいる。だから、キャスト(割り当て)しても基底クラスポインタは、基底の部分がしっかりとあるので、キャストできる。派生クラスから基底クラスにキャストは出来ます。その逆は出来ません。派生クラスだけが持っている何だか知りえない何かがあるのに基底クラスのポインタは派生クラスのポインタになれない。そういうことなのです。 で、何の役にたつのだい?そのアップキャストやらは… 基底クラスが派生クラスに対して働きかけるイメージです。基底クラスが、派生クラスを選ぶ。基底クラスがいろいろな派生クラスと結びつくことができて、基底クラスが持っている動作と派生クラス1の動作をまとめた動作、基底クラスが持っている動作と派生クラス2の動作をまとめた動作、基底クラスが持っている動作と派生クラス3の動作をまとめた動作、…基底クラスが持っている動作と派生クラスnの動作をまとめた動作のように、基底クラスは動きを切り替えることが出来るという技術です。何にでもなれるという意味でアップキャストはポリモーフィズム(polymorphism=多相性・多態性・多様性)と呼ばれている働きを実現させるモノになっています。 継承において、派生クラスは、基底クラスを引き継いで利用する。アップキャストで基底クラスはいろいろぶら下がった派生クラス(派生クラスからは基底クラスが上でアップキャスト)を選んで、いろいろな機能を利用するという関係です。それは、派生クラスのポインタ変数が、基底クラスのポインタ変数に受け渡すという動作(アップキャスト)によって実現される。自分はいまだに上とか下とかという言い方に違和感を感じていますが、そういう名前が付けられたということは理解したし、納得したという感じです。いわせてみれば、自分が折れてやった。そういう偉そうな立場に立ってみた。そして、見下される。そういうことだ。そう、そういうことなんだ。 ポインタ変数の同士の受け渡しで実現すると表現しましたが、実際には、派生クラス型の変数→基底クラス型の参照変数という受け渡しでもアップキャストが出来ます。これは関数の引数でも受け渡せますし、アドレスの代入のような形式でも受け渡せます。記述パターンは何種類かあると考えてよいです。 では、ちょっとした使い方のサンプルプログラムを作ってみます。継承のサンプル同様に単純機能でアップキャストを利用します。継承のサンプルによく似た考え方でサブスクリプションの仕組みの基本料金x月を計算する基底クラスに、オプション料金1つを足す機能がある派生クラスがこれまでのサンプルで、もう二つくらい派生クラスを作って、オプション料金をいくつか格納した配列を受け取って足し合わせる機能と基本料金が半額に割引きされる派生クラスを作ってみたいと思います。 いくら単純とはいえ3つも派生クラスを作れば、プログラムは長くなりますけどね。 <span style="color: #ffffff; background-color: #555555; padding: 0px 5px 0px 5px; display: inline-block;">cpp <span>(</span>基底クラス BaseInheritance.h<span>)</span><!-- padding 上 右 下 左--> <syntaxhighlight2 lang="cpp" line> #ifndef __BASEINHERITANCE_H_YONET__ #define __BASEINHERITANCE_H_YONET__ #if _MSC_VER > 1000 #pragma once #endif class CBaseInheritance { protected: int m_iBaseMoney = 0; int m_iBaseMonth = 0; int mf_iBaseSumMoney(); public: CBaseInheritance(); CBaseInheritance(int iArgBaseMoney, int iArgBaseMonth); ~CBaseInheritance(); void mf_vBaseDispValue(); }; #endif </syntaxhighlight2> <span style="color: #ffffff; background-color: #555555; padding: 0px 5px 0px 5px; display: inline-block;">cpp <span>(</span>基底クラス BaseInheritance.cpp<span>)</span><!-- padding 上 右 下 左--> <syntaxhighlight2 lang="cpp" line> #include "pch.h" #include "BaseInheritance.h" CBaseInheritance::CBaseInheritance() { printf("Constructor:CBaseInheritance()\n"); } CBaseInheritance::CBaseInheritance(int iArgBaseMoney, int iArgBaseMonth) { m_iBaseMoney = iArgBaseMoney; m_iBaseMonth = iArgBaseMonth; printf("Constructor:CBaseInheritance(int,int)\n"); } CBaseInheritance::~CBaseInheritance() { printf("Destructor:~CBaseInheritance()\n"); } int CBaseInheritance::mf_iBaseSumMoney() { return (m_iBaseMoney * m_iBaseMonth); } void CBaseInheritance::mf_vBaseDispValue() { int iSumMoney = mf_iBaseSumMoney(); printf("BaseMoney=%d\n", iSumMoney); } </syntaxhighlight2> 基底クラスは月額料金と加入月数を受け取って、合計金額を返すという機能を持っています。サブスクリプション(定額契約)の基本ですね。コンストラクタ(CBaseInheritance::CBaseInheritance)は受け取った引数を月額料金変数(m_iBaseMoney)と利用月の変数(m_iBaseMonth)に格納しています。合計金額を算出するメンバ関数(CBaseInheritance::mf_iBaseSumMoney)で、月額料金×利用月の結果を返却します。合計金額表示用の関数は合計金額を算出するメンバ関数を呼び出して、printf分で表示します。std::cinやstd::coutというのがc++の入出力標準<iostream>ヘッダファイルを呼び出して利用するのが一般的ですが、あえて<cstdio>で使えるprintfを利用しています。これは管理人の好みです。cin、coutって>>とか<<とかわかりずらくて、人に見せるプログラムで使う時は、こういうものを使うのが好きではないです。そもそも分かり辛い記事が、ますます分かり辛くなってしまうような気がします。 それで派生クラスは以下のようなものを作りました。まずは、オプション料金を一つだけ受け取るクラス。 <span style="color: #ffffff; background-color: #555555; padding: 0px 5px 0px 5px; display: inline-block;">cpp <span>(</span>派生クラス DeriveInheritance.h<span>)</span><!-- padding 上 右 下 左--> <syntaxhighlight2 lang="cpp" line> #ifndef __DERIVEINHERITANCE_H_YONET__ #define __DERIVEINHERITANCE_H_YONET__ #if _MSC_VER > 1000 #pragma once #endif #include "BaseInheritance.h" class CDeriveInheritance :public CBaseInheritance { private: int m_iDeriveOption = 0; int m_iDeriveMoney = 0; public: CDeriveInheritance(int iArgMoney, int iArgMonth); CDeriveInheritance(int iArgMoney, int iArgMonth, int iArgOption); ~CDeriveInheritance(); void mf_vDeriveDispValue(); int mf_iDeriveSumMoney(); void mf_vDeriveSetOptionValue(int iArgOptionValue); }; #endif </syntaxhighlight2> <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> #include "pch.h" #include "DeriveInheritance.h" CDeriveInheritance::CDeriveInheritance(int iArgMoney, int iArgMonth) :CBaseInheritance(iArgMoney, iArgMonth){ printf("Constructor:CDriveInheritance(int,int)\n"); } CDeriveInheritance::CDeriveInheritance(int iArgMoney, int iArgMonth, int iArgOption) :CBaseInheritance(iArgMoney, iArgMonth) { printf("Constructer:CDeriveInheritance(int,int,int)\n"); m_iDeriveOption = iArgOption; } CDeriveInheritance::~CDeriveInheritance() { printf("Destructer:~CDeriveInheritance()\n"); } void CDeriveInheritance::mf_vDeriveDispValue() { int iSumMoney = mf_iDeriveSumMoney(); printf("DeriveMoney=%d\n", iSumMoney); } void CDeriveInheritance::mf_vDeriveSetOptionValue(int iArgOptionMoney) { m_iDeriveOption = iArgOptionMoney; } int CDeriveInheritance::mf_iDeriveSumMoney() { return mf_iBaseSumMoney() + m_iDeriveOption; } </syntaxhighlight2> Deriveは「派生する」という意味で、CDeriveInheritance(int iArgMoney, int iArgMonth, int iArgOption)は3つの引数を受け取って、基本料金、利用月を基底クラスに初期化リストでそのまま渡しています。そして派生クラスだけが扱う3つめのオプション料金をm_iDeriveOptionに格納しています。引数が2つのBaseクラスと基底クラスと同じ役割のコンストラクタCDeriveInheritance(int iArgMoney, int iArgMonth)があります。2つの引数をそのまま、基底クラスに初期化リストを渡しています。後から、オプション料金だけを指定できる関数CDeriveInheritance::mf_vDeriveSetOptionValueもあります。オプション料も含めた合計金額はmf_iDeriveSumMoneyで計算して返却され、mf_vDeriveDispValueで利用され、printfで合計金額を出力します。 2つめの派生クラスはオプション金額を複数、配列によって受け取るものを作ります。以下のとおりです。 <span style="color: #ffffff; background-color: #555555; padding: 0px 5px 0px 5px; display: inline-block;">cpp <span>(</span>派生クラス DeriveInheritance.h<span>)</span><!-- padding 上 右 下 左--> <syntaxhighlight2 lang="cpp" line> #ifndef __DERIVEARRINHERITANCE_H_YONET__ #define __DERIVEARRINHERITANCE_H_YONET__ #if _MSC_VER > 1000 #pragma once #endif #include "BaseInheritance.h" class CDeriveArrInheritance :public CBaseInheritance { private: int m_iDeriveOption = 0; int m_iDeriveMoney = 0; int m_iSize = 0; int* m_piDeriveOptionArr = nullptr; void mf_vDeriveSumMoney(); public: CDeriveArrInheritance(int iArgMoney, int iArgMonth); CDeriveArrInheritance(int iArgMoney, int iArgMonth, int* piArgOption,int iSize); ~CDeriveArrInheritance(); void mf_vDeriveArrDispValue(); void mf_vDeriveSetArrOption(int* piArgOption); int mf_iDeriveGetArrOption(); }; #endif </syntaxhighlight2> <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> #include "pch.h" #include "DeriveArrInheritance.h" CDeriveArrInheritance::CDeriveArrInheritance(int iArgMoney, int iArgMonth) :CBaseInheritance(iArgMoney, iArgMonth){ printf("Constructor:CDriveArrInheritance(int,int)\n"); } CDeriveArrInheritance::CDeriveArrInheritance(int iArgMoney, int iArgMonth, int* piArgOption,int iArgSize) :CBaseInheritance(iArgMoney, iArgMonth) { printf("Constructer:CDeriveArrInheritance(int,int,int*,int)\n"); m_iSize = iArgSize; mf_vDeriveSetArrOption(piArgOption); } CDeriveArrInheritance::~CDeriveArrInheritance() { printf("Destructer:~CDeriveArrInheritance()\n"); delete[] m_piDeriveOptionArr; } int CDeriveArrInheritance::mf_iDeriveGetArrOption() { return m_iDeriveOption; } void CDeriveArrInheritance::mf_vDeriveArrDispValue() { int iSumMoney = mf_iDeriveGetArrOption() + mf_iBaseSumMoney(); printf("DeriveMoney=%d\n", iSumMoney); } void CDeriveArrInheritance::mf_vDeriveSumMoney() { for (int iScan = 0; iScan < m_iSize; iScan++) { m_iDeriveOption += m_piDeriveOptionArr[iScan]; } } void CDeriveArrInheritance::mf_vDeriveSetArrOption(int* piArgOption) { m_piDeriveOptionArr = new int[m_iSize]; for (int iScan = 0; iScan < m_iSize; iScan++) { m_piDeriveOptionArr[iScan] = piArgOption[iScan]; } mf_vDeriveSumMoney(); } </syntaxhighlight2> [[C PlusPlus#C++からの技術|C++]]に戻る
テンプレート:記事名の制約
(
ソースを閲覧
)
Cpp クラス 継承 アップキャスト
に戻る。
個人用ツール
ログイン
名前空間
ページ
議論
変種
表示
閲覧
ソースを表示
履歴表示
操作
検索
案内
メインページ
コミュニティ・ポータル
最近の出来事
最近の更新
おまかせ表示
ヘルプ
ツールボックス
リンク元
関連ページの更新状況
特別ページ