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>
31行: 31行:
 
 継承において、派生クラスは、基底クラスを引き継いで利用する。アップキャストで基底クラスはいろいろぶら下がった派生クラス(派生クラスからは基底クラスが上でアップキャスト)を選んで、いろいろな機能を利用するという関係です。それは、派生クラスのポインタ変数が、基底クラスのポインタ変数に受け渡すという動作(アップキャスト)によって実現される。自分はいまだに上とか下とかという言い方に違和感を感じていますが、そういう名前が付けられたということは理解したし、納得したという感じです。いわせてみれば、自分が折れてやった。そういう偉そうな立場に立ってみた。そして、見下される。そういうことだ。そう、そういうことなんだ。
 
 継承において、派生クラスは、基底クラスを引き継いで利用する。アップキャストで基底クラスはいろいろぶら下がった派生クラス(派生クラスからは基底クラスが上でアップキャスト)を選んで、いろいろな機能を利用するという関係です。それは、派生クラスのポインタ変数が、基底クラスのポインタ変数に受け渡すという動作(アップキャスト)によって実現される。自分はいまだに上とか下とかという言い方に違和感を感じていますが、そういう名前が付けられたということは理解したし、納得したという感じです。いわせてみれば、自分が折れてやった。そういう偉そうな立場に立ってみた。そして、見下される。そういうことだ。そう、そういうことなんだ。
  
 +
 +
 ポインタ変数の同士の受け渡しで実現すると表現しましたが、実際には、派生クラス型の変数→基底クラス型の参照変数という受け渡しでもアップキャストが出来ます。これは関数の引数でも受け渡せますし、アドレスの代入のような形式でも受け渡せます。記述パターンは何種類かあると考えてよいです。
 +
 +
 +
 では、ちょっとした使い方のサンプルプログラムを作ってみます。継承のサンプル同様に単純機能でアップキャストを利用します。継承のサンプルによく似た考え方でサブスクリプションの仕組みの基本料金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 m_iMoney = 0;
 +
    int mf_iBaseSumMoney();
 +
public:
 +
    CBaseInheritance();
 +
    CBaseInheritance(int iArgBaseMoney, int iArgBaseMonth);
 +
    ~CBaseInheritance();
 +
    void mf_vDispValue();
 +
    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 start=100>
 +
#include "pch.h"
 +
#include "BaseInheritance.h"
 +
 +
CBaseInheritance::CBaseInheritance() {
 +
    printf("Constructor:CBaseInheritance()\n");
 +
}
 +
 +
CBaseInheritance::CBaseInheritance(int iArgBaseMoney, int iArgBaseMonth)
 +
                :m_iBaseMoney(iArgBaseMoney)
 +
                ,m_iBaseMonth(iArgBaseMonth)
 +
                ,m_iMoney(0) {
 +
    printf("Constructor:CBaseInheritance(int,int)\n");
 +
}
 +
 +
CBaseInheritance::~CBaseInheritance() {
 +
    printf("Destructor:~CBaseInheritance()\n");
 +
}
 +
 +
int CBaseInheritance::mf_iBaseSumMoney() {
 +
    m_iMoney = m_iBaseMoney * m_iBaseMonth;
 +
    return m_iMoney;
 +
}
 +
 +
void CBaseInheritance::mf_vDispValue() {
 +
    int iSumMoney = mf_iBaseSumMoney();
 +
    printf("BaseMoney=%d total=%d\n", iSumMoney, m_iMoney);
 +
}
 +
 +
void CBaseInheritance::mf_vBaseDispValue() {
 +
    int iSumMoney = m_iBaseMoney * m_iBaseMonth;
 +
    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 start=200>
 +
#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_iDeriveOption();
 +
    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 start=300>
 +
#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;
 +
        m_iMoney = mf_iBaseSumMoney() + m_iDeriveOption;
 +
}
 +
 +
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_iDeriveOption() {
 +
return m_iDeriveOption;
 +
}
 +
 +
 +
int CDeriveInheritance::mf_iDeriveSumMoney() {
 +
m_iMoney = mf_iBaseSumMoney() + m_iDeriveOption;
 +
return m_iMoney;
 +
}
 +
 +
 +
</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>配列入力の派生クラス DeriveArrInheritance.h<span>)</span><!-- padding 上 右 下 左-->
 +
<syntaxhighlight2 lang="cpp" line start=400>
 +
#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;
 +
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_iDeriveSumMoney();
 +
    int  mf_iDeriveGetArrOption();
 +
};
 +
#endif
 +
</syntaxhighlight2>
 +
 +
<span style="color: #ffffff; background-color: #555555; padding: 0px 5px 0px 5px; display: inline-block;">cpp <span>(</span>配列入力の派生クラス DeriveArrInheritance.cpp<span>)</span><!-- padding 上 右 下 左-->
 +
<syntaxhighlight2 lang="cpp" line start=500>
 +
#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);
 +
}
 +
 +
int CDeriveArrInheritance::mf_iDeriveSumMoney() {
 +
for (int iScan = 0; iScan < m_iSize; iScan++) {
 +
m_iDeriveOption += m_piDeriveOptionArr[iScan];
 +
}
 +
m_iMoney = m_iDeriveOption + mf_iBaseSumMoney();
 +
return m_iMoney;
 +
}
 +
 +
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_iDeriveSumMoney();
 +
}
 +
 +
 +
</syntaxhighlight2>
 +
 +
 +
 配列入力を受け付けるコンストラクタCDeriveArrInheritance::CDeriveArrInheritance(int iArgMoney, int iArgMonth, int* piArgOption,int iArgSize)は3つめの引数が、その配列アドレスを受け取り、配列の大きさを4つめの引数で受け取りメンバ変数m_iSizeへ格納します。配列はメンバ関数mf_vDeriveSetArrOption(piArgOption)でそのまま横流しします。mf_vDeriveSetArrOptionでは、配列を自身のクラスの中でコピーするようにm_piDeriveOptionArr = new int[m_iSize]で動的に受け取った大きさの分の配列として生成します。そしてfor分の中で配列のひと要素づつをコピーしていきます。mf_vDeriveSumMoney()を呼び、配列のひと要素づつを足し合わせて、m_iDeriveOptionへ合計を記憶します。CDeriveArrInheritance::mf_vDeriveArrDispValueで基底クラスの月額料金×利用月の合計と自身のクラスで計算したオプション合計金額m_iDeriveOptionに格納し、その値を返却するCDeriveArrInheritance::mf_iDeriveGetArrOptionを使って足し合わせた値をprintf文で表示しています。デストラクタで動的に生成したm_piDeriveOptionArrが保持する実体を消去する処理を入れています。配列を使うとちょっとプログラムが長くなりますね。
 +
 +
 +
 次に3つめの派生クラスの基底クラスの料金を割り引くクラスを作ります。
 +
 +
<span style="color: #ffffff; background-color: #555555; padding: 0px 5px 0px 5px; display: inline-block;">cpp <span>(</span>割引の派生クラス DeriveDiscountInheritance.h<span>)</span><!-- padding 上 右 下 左-->
 +
<syntaxhighlight2 lang="cpp" line start=600>
 +
#ifndef __DERIVEDISCOUNTINHERITANCE_H_YONET__
 +
#define __DERIVEDISCOUNTINHERITANCE_H_YONET__
 +
#if _MSC_VER > 1000
 +
#pragma once
 +
#endif
 +
#include "BaseInheritance.h"
 +
 +
class CDeriveDiscountInheritance :public CBaseInheritance {
 +
private:
 +
    int m_iDeriveOption = 0;
 +
    int m_iDeriveMoney = 0;
 +
    double m_dRate = 0;
 +
public:
 +
    CDeriveDiscountInheritance(int iArgMoney, int iArgMonth);
 +
    CDeriveDiscountInheritance(int iArgMoney, int iArgMonth, double dArgRate);
 +
    ~CDeriveDiscountInheritance();
 +
    void mf_vDeriveDiscountDispValue();
 +
 +
    void mf_iDeriveSetDiscount(double dRate);
 +
    int mf_iDeriveSumMoney();
 +
};
 +
#endif
 +
</syntaxhighlight2>
 +
 +
<span style="color: #ffffff; background-color: #555555; padding: 0px 5px 0px 5px; display: inline-block;">cpp <span>(</span>割引の派生クラス DeriveDiscountInheritance.cpp<span>)</span><!-- padding 上 右 下 左-->
 +
<syntaxhighlight2 lang="cpp" line start=700>
 +
#include "pch.h"
 +
#include "DeriveDiscountInheritance.h"
 +
 +
CDeriveDiscountInheritance::CDeriveDiscountInheritance(int iArgMoney, int iArgMonth)
 +
              :CBaseInheritance(iArgMoney, iArgMonth){
 +
printf("Constructor:CDriveDiscountInheritance(int,int)\n");
 +
}
 +
 +
CDeriveDiscountInheritance::CDeriveDiscountInheritance(int iArgMoney, int iArgMonth, double dArgRate)
 +
              :CBaseInheritance(iArgMoney, iArgMonth) {
 +
printf("Constructer:CDeriveDiscountInheritance(int,int,double)\n");
 +
m_dRate = dArgRate;
 +
mf_iDeriveSumMoney();
 +
}
 +
 +
CDeriveDiscountInheritance::~CDeriveDiscountInheritance() {
 +
printf("Destructer:~CDeriveDiscountInheritance()\n");
 +
}
 +
 +
 +
void CDeriveDiscountInheritance::mf_iDeriveSetDiscount(double dArgRate) {
 +
m_dRate = dArgRate;
 +
mf_iDeriveSumMoney();
 +
}
 +
 +
void CDeriveDiscountInheritance::mf_vDeriveDiscountDispValue() {
 +
int iSumMoney = mf_iDeriveSumMoney();
 +
printf("DeriveMoney=%d\n", iSumMoney);
 +
}
 +
 +
int CDeriveDiscountInheritance::mf_iDeriveSumMoney() {
 +
m_iMoney = (int)(m_dRate * mf_iBaseSumMoney() + 0.5);
 +
return m_iMoney;
 +
}
 +
</syntaxhighlight2>
 +
 +
 コンストラクタCDeriveDiscountInheritance(int iArgMoney, int iArgMonth, double dArgRate)は3つめの引数で割引の率を受け取ります。5割引きなら0.5を受け付ける形式です。50%の50とかではないです。そして、割合をm_dRateへ格納します。2つの引数だけ受け取るコンストラクタを使って、後から割合だけを受け取るCDeriveDiscountInheritance::mf_iDeriveSetDiscountというメンバ関数もあります。そしてCDeriveDiscountInheritance::mf_vDeriveDiscountDispValue()で割引した後の金額を表示します。m_dRateはdouble型なので、計算結果が小数まで計算されます。これを四捨五入するために0.5を足して、(int)(計算結果小数)のようにキャストして小数部を切り落としています。通常のお店の計算では四捨五入ではなく、切り捨てされるのが普通ですが、あえて四捨五入にしてみました。小数点第何位や十、百、千の位とかで四捨五入する場合はもっと違うカタチで四捨五入をしなければならないです。ひとつの方法としては、四捨五入する位置が小数第一位に持ってくるようにを一度、10のN乗倍してから、上記と同じ方法で切り落として、そしてもう一度 10の-N乗倍して、元に戻すという感じです。これだと桁あふれや桁落ちが発生しやすいので、別の方法の方が良かったりします。で、いろいろ考えて自分で作ろうとしてしまいがちですが、標準関数で四捨五入をする関数が既に用意されていたりもします。
 +
 +
 +
 これで派生クラスも出そろいました。普通に派生クラスを利用しようとすると以下のようになります。
 +
 +
<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=800>
 +
#include "pch.h"
 +
#include "BaseInheritance.h"
 +
#include "DeriveInheritance.h"
 +
#include "DeriveArrInheritance.h"
 +
#include "DeriveDiscountInheritance.h"
 +
 +
int main() {
 +
 +
    CBaseInheritance objCBaseInheritance(3900, 12);
 +
    objCBaseInheritance.mf_vDispValue();
 +
 +
    CDeriveInheritance objCDeriveInheritance2Arg(2900, 12);
 +
    objCDeriveInheritance2Arg.mf_vDeriveSetOptionValue(600);
 +
    objCDeriveInheritance2Arg.mf_vDispValue();
 +
 +
    CDeriveInheritance objCDeriveInheritance3Arg(2900, 12, 600);
 +
    objCDeriveInheritance3Arg.mf_vDispValue();
 +
 +
    int piOptionArr[] = { 300, 500, 400, 390 };
 +
    int iSize = (int)(sizeof piOptionArr / sizeof(*piOptionArr));
 +
    CDeriveArrInheritance objCDeriveArrInheritance(3900, 12, piOptionArr, iSize);
 +
    objCDeriveArrInheritance.mf_vDispValue()();
 +
 +
    CDeriveDiscountInheritance objCDeriveDiscountInheritance(3900, 12, 0.5);
 +
    objCDeriveDiscountInheritance.mf_vDispValue();
 +
 +
    return 0;
 +
 +
}
 +
</syntaxhighlight2>
 +
 +
 アップキャストの話をしている現段階では、上記のメイン関数は普通に派生クラスを使っているだけなので、特に説明もいらないでしょう。基底クラス→一つしかオプション価格を受け取らない派生クラス→複数のオプション価格をうけとる配列を扱う派生クラス→割引を扱う派生クラスの順に動かしています。動かした結果は以下のとおりです。
 +
 +
<span style="color: #ffffff; background-color: #555555; padding: 0px 5px 0px 5px; display: inline-block;">実行結果<!-- padding 上 右 下 左-->
 +
<syntaxhighlight2 lang="text" line start=900>
 +
Constructor:CBaseInheritance(int,int)
 +
BaseMoney=46800, total=35700
 +
Constructor:CBaseInheritance(int,int)
 +
Constructor:CDriveInheritance(int,int)
 +
BaseMoney=34800, total=35700
 +
Constructor:CBaseInheritance(int,int)
 +
Constructer:CDeriveInheritance(int,int,int)
 +
BaseMoney=34800, total=35700
 +
Constructor:CBaseInheritance(int,int)
 +
Constructer:CDeriveArrInheritance(int,int,int*,int)
 +
BaseMoney=46800, total=48390
 +
Constructor:CBaseInheritance(int,int)
 +
Constructer:CDeriveDiscountInheritance(int,int,double)
 +
BaseMoney=46800, total=23400
 +
Destructer:~CDeriveDiscountInheritance()
 +
Destructor:~CBaseInheritance()
 +
Destructer:~CDeriveArrInheritance()
 +
Destructor:~CBaseInheritance()
 +
Destructer:~CDeriveInheritance()
 +
Destructor:~CBaseInheritance()
 +
Destructer:~CDeriveInheritance()
 +
Destructor:~CBaseInheritance()
 +
Destructor:~CBaseInheritance()
 +
</syntaxhighlight2>
 +
 +
 コンストラクタとデストラクタがどういう風に動作するのかがわかるように呼び出されたら、printfで何が呼ばれたかわかるようにしています。勝手に呼ばれることも多いので、わかりやすいです。
 +
 +
 +
 で、これを元にアップキャストって奴をやってみようと思います。こんな行き当たりばったりでサンプルを作っているので、上手いこと行くかはわかりませんが、メイン関数を作り直してみます。
 +
 +
<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=1000>
 +
#include "pch.h"
 +
#include "BaseInheritance.h"
 +
#include "DeriveInheritance.h"
 +
#include "DeriveArrInheritance.h"
 +
#include "DeriveDiscountInheritance.h"
 +
 +
CBaseInheritance* pCBaseInheritanceUpcastF(int iSelectDerive) {
 +
    int piOptionUpcastArr[] = { 800, 200, 500, 220 };
 +
    int iSize = (int)(sizeof piOptionUpcastArr / sizeof(*piOptionUpcastArr));
 +
    switch (iSelectDerive) {
 +
        case 1:
 +
            return new CDeriveInheritance(2900, 12);
 +
        case 2:
 +
            return new CDeriveArrInheritance(2900, 12, piOptionUpcastArr, iSize);
 +
        default:
 +
            return new CDeriveDiscountInheritance(3900, 12, 0.5);
 +
    }
 +
};
 +
 +
int main() {
 +
 +
    CBaseInheritance* pCBaseInheritanceUpcast;
 +
    pCBaseInheritanceUpcast = pCBaseInheritanceUpcastF(1);
 +
    pCBaseInheritanceUpcast->mf_vDispValue();
 +
    pCBaseInheritanceUpcast = pCBaseInheritanceUpcastF(2);
 +
    pCBaseInheritanceUpcast->mf_vDispValue();
 +
    pCBaseInheritanceUpcast = pCBaseInheritanceUpcastF(3);
 +
    pCBaseInheritanceUpcast->mf_vDispValue();
 +
    delete pCBaseInheritanceUpcast;
 +
 +
    return 0;
 +
 +
}
 +
</syntaxhighlight2>
 +
 +
 このようにサンプルのメインプログラムだけを変えました。基底クラスのポインタ変数を作って、派生クラスを動かした結果を受け取っています。プログラムの中で、動かす派生クラスを選べて、基底クラスを利用するカタチにしてみました。派生クラスを生成するとオブジェクトが出来当たりますが、そのオブジェクトのポインタを返却(return)したときに関数の返却値の型が基底クラスのポインタ型になっています。そして、結果をそのままメイン関数で受け取るので、アップキャストはreturnの型から返却値の型に変換されているところで起こっています。
 +
 +
 +
 実行した結果は以下のようになります。
 +
 +
 +
<span style="color: #ffffff; background-color: #555555; padding: 0px 5px 0px 5px; display: inline-block;">実行結果<!-- padding 上 右 下 左-->
 +
<syntaxhighlight2 lang="text" line start=1100>
 +
Constructor:CBaseInheritance(int,int)
 +
Constructor:CDriveInheritance(int,int)
 +
BaseMoney=34800, total=35700
 +
Constructor:CBaseInheritance(int,int)
 +
Constructer:CDeriveArrInheritance(int,int,int*,int)
 +
BaseMoney=34800, total=36520
 +
Constructor:CBaseInheritance(int,int)
 +
Constructer:CDeriveDiscountInheritance(int,int,double)
 +
BaseMoney=46800, total=23400
 +
Destructor:~CBaseInheritance()
 +
</syntaxhighlight2>
 +
 +
 +
 ちゃんとアップキャストされても、ちゃんと基底クラスのメンバ関数が派生クラスで動作した内容を反映して動いています。でもデストラクタは、呼ばれていないですね。残念なプログラムになっているようです。次の「継承 デストラクタ」の記事でこの問題について対応したいと思います。今のままでは動的に変数をクラス内で生成しているCDeriveArrInheritanceを使ったときにメモリリークが起こってしまいます。動的に確保したint型の配列メモリをプログラム終了後も解放しようとしない現象です。実際にはプログラム終了時にRAII(Resource Acquisition Is Initialization)という機能によって、ほとんどの場合メモリは解放されます。C++では、smart pointerというものに包み込まれた状態になり、動的に確保したメモリ領域を監視しているので、動的に確保されたメモリは適切に開放されます。たとえ途中で例外処理が発生してプログラムが落っこちた場合でも、解放されます。でも、ちゃんとやった方がいいです。上記のような状態は決してメモリリークがおきないとは保証されないので、ちゃんとやった方がいいのです。でも、アップキャストしたら元々のデストラクタが呼ばれないのは困ったものです。うっかりすると危険だわ。
  
 
[[C PlusPlus#C++からの技術|C++]]に戻る
 
[[C PlusPlus#C++からの技術|C++]]に戻る

2021年2月6日 (土) 00:00時点における最新版



個人用ツール
名前空間

変種
操作
案内
ツールボックス