Cpp クラス 継承 コンストラクタ

提供:yonewiki

C++に戻る


本来の表記は「C++(Cpp) クラス 継承 コンストラクタ」です。この記事に付けられた題名はテンプレート:記事名の制約から不正確なものとなっています。

※このページではC++にのみ存在する機能として、記事タイトルがC++ クラス 継承 コンストラクタになっています。

クラス 継承 コンストラクタ

 継承におけるコンストラクタの挙動は先のCpp クラス 継承でも簡単に示した通りで、デストラクタまで踏まえると呼び出される順番は派生クラスのオブジェクト生成後、まずは基底クラスのコンストラクタが呼ばれて、次に派生クラスのコンストラクタが呼ばれます。そして派生クラスのオブジェクト消滅の寸前で、派生クラスのデストラクタが呼ばれて、次に基底クラスのデストラクタが呼ばれ、そして、オブジェクトが消滅します。


 この時、派生クラスに引数がある場合でも、基底クラスでは引数を取らないコンストラクタが呼ばれます。そして、派生クラスが初期化リストをもたないコンストラクタがある場合は、引数を取らないデフォルトコンストラクタは何も処理をしないとしても明記しないと継承時に派生クラスは独自の引数のコンストラクタを持つことが出来ないのでした。


 これでは、派生クラスは常にコンストラクタを自前で準備するか、基底クラスの引数無しコンストラクタを使わなければならず、継承の旨味みたいなものが少し目減りして感じてしまいます。大丈夫、基底クラスの引数付きコンストラクタを呼び出すことは出来ます。派生クラスの初期化指定子で基底クラスのコンストラクタを記述されば良いです。では、Cpp クラス 継承で使ったプログラムを少し改造して無理矢理呼び出して使ってみるプログラムを書きましょう。


cpp (基底クラス BaseInheritance.h)

#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_fiBaseSumMoney();
public:
    CBaseInheritance();
    CBaseInheritance(int iArgBaseMoney, int iArgBaseMonth);
    ~CBaseInheritance();
    void m_fvBaseDispValue();
};
#endif


cpp (基底クラス BaseInheritance.cpp)

#include <cstdio>
#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");
}
void CBaseInheritance::m_fvBaseDispValue() {
    int iSumMoney = m_fiBaseSumMoney();
    printf("BaseMoney=%d\n", iSumMoney);
}
int CBaseInheritance::m_fiBaseSumMoney() {
    return (m_iBaseMoney * m_iBaseMonth);
}


 基底クラスはいじる必要はなさそうです、派生クラスの変更が大事ですね。


cpp (派生クラス DeriveInheritance.h)

#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_iOptionMoney = 0;
    int m_iDeriveMoney = 0;
public:
    CDeriveInheritance(int iArgDeriveMoney, int iArgDeriveMonth, int iArgOptionMoney);
    CDeriveInheritance(int iArgMoney, int iArgMonth) : CBaseInheritance(iArgMoney, iArgMonth) { printf("Constructor:CDriveInheritance(int,int)\n"); }
    ~CDeriveInheritance();
    void m_fvDeriveDispValue();
    int m_fiDeriveSumMoney();
    void m_fvSetDeriveOptionValue(int iArgOptionValue);
};
#endif

cpp (DeriveInheritance.cpp)

#include "pch.h"
#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;
	m_iDeriveMoney = 0;
}

CDeriveInheritance::~CDeriveInheritance() {
	printf("Destructer:~CDeriveInheritance()\n");
}

void CDeriveInheritance::m_fvDeriveDispValue() {
	int iSumMoney = m_fiDeriveSumMoney();
	printf("DeriveMoney=%d\n", iSumMoney);
}

void CDeriveInheritance::m_fvSetDeriveOptionValue(int iArgOptionMoney) {
	m_iOptionMoney = iArgOptionMoney;
}

int CDeriveInheritance::m_fiDeriveSumMoney() {
	return m_fiBaseSumMoney() + m_iOptionMoney;
}

 という具合の変更を加えました。この変更において重要なのは基底クラスの引数2個呼び出しをするということです。その役割に担っているのが、派生クラスのヘッダファイル部の213行目です。

CDeriveInheritance(int iArgMoney, int iArgMonth) : CBaseInheritance(iArgMoney, iArgMonth) { printf("Constructor:CDriveInheritance(int,int)\n"); }

 上記の:の後ろが初期化リストで、基底クラスのコンストラクタ名に引数の名前を派生クラスのものと一致させて記述します。このとき型名は省略して表記します。ヘッダファイル名で初期化リストを付けた場合は派生クラスのコンストラクタはインライン関数にしなければならないです。


 これだけで、基底クラスに横流しする形で、同じ引数のものが呼び出せます。


 あとは、どうでもよいプログラムの中身を少しいじりました。引数を2つしか受け取らない場合、派生クラスの必須機能だった、サブスクリプション価格×利用月数にオプションの価格を足し合わせないといけないのですが、オプションの価格がわかりません。オプションの価格を受けとる専用の関数を派生クラスに追加しました。CDeriveInheritance::m_fvSetDeriveOptionValueというメンバ関数です。オプション価格のメンバ関数に単独で呼び出された引数を受け取る関数になっています。


 これらの変更を加えた派生クラスを利用するメイン関数は以下のようになります。

cpp (DeriveInheritance.cpp)

#include "pch.h"
#include "DeriveInheritance.h"

int main() {
    CDeriveInheritance objCDeriveInheritance(3900, 12);
    objCDeriveInheritance.m_fvSetDeriveOptionValue(600);
    objCDeriveInheritance.m_fvDeriveDispValue();

    return 0;
}


 となります。無理矢理、基底クラスのコンストラクタを使えるようにしたので、メイン関数の手数が増えました。処理結果は以下のようになります。

処理結果

Constructor:CBaseInheritance(int,int)
Constructor:CDriveInheritance(int,int)
DeriveMoney=47400
Destructer:~CDeriveInheritance()
Destructor:~CBaseInheritance()

 のようになって、たしかに、引数を2個受け取るコンストラクタが派生クラスおよび基底クラスで呼び出されています。初期化処理の全部を基底クラスにまかせることができるなら、インライン関数にして{};のようにして派生クラスは何もしないにするでしょう。逆に、もっといろんなことを派生クラスは派生クラスで初期化しなければならないなら派生クラスのプログラム記述部に処理内容を記述するべきでしょう。その場合は派生クラスのヘッダファイルとプログラムファイルは以下のようになります。


cpp (派生クラス DeriveInheritance.h)

#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_iOptionMoney = 0;
    int m_iDeriveMoney = 0;
public:
    CDeriveInheritance(int iArgDeriveMoney, int iArgDeriveMonth, int iArgOptionMoney);
    CDeriveInheritance(int iArgMoney, int iArgMonth);
    ~CDeriveInheritance();
    void m_fvDeriveDispValue();
    int m_fiDeriveSumMoney();
    void m_fvSetDeriveOptionValue(int iArgOptionValue);
};
#endif

cpp (DeriveInheritance.cpp)

#include "pch.h"
#include "DeriveInheritance.h"

CDeriveInheritance::CDeriveInheritance(int iArgMoney, int iArgMonth) : CBaseInheritance(iArgMoney, iArgMonth) { 
        printf("Constructor:CDriveInheritance(int,int)\n"); 
}

CDeriveInheritance::CDeriveInheritance(int iArgDeriveMoney, int iArgDeriveMonth, int iArgOptionMoney) {
	printf("Constructer:CDeriveInheritance(int,int,int)\n");
	m_iBaseMoney = iArgDeriveMoney;
	m_iBaseMonth = iArgDeriveMonth;
	m_iOptionMoney = iArgOptionMoney;
	m_iDeriveMoney = 0;
}

CDeriveInheritance::~CDeriveInheritance() {
	printf("Destructer:~CDeriveInheritance()\n");
}

void CDeriveInheritance::m_fvDeriveDispValue() {
	int iSumMoney = m_fiDeriveSumMoney();
	printf("DeriveMoney=%d\n", iSumMoney);
}

void CDeriveInheritance::m_fvSetDeriveOptionValue(int iArgOptionMoney) {
	m_iOptionMoney = iArgOptionMoney;
}

int CDeriveInheritance::m_fiDeriveSumMoney() {
	return m_fiBaseSumMoney() + m_iOptionMoney;
}

 となります。ヘッダファイル側の613行目のようにインライン関数側には初期化リストは書かず、プログラム側の703行目のようにで初期化リストとともに処理内容を記述します。


 派生クラスのコンストラクタで初期化リストが、基底クラスのコンストラクタの引数付きで、初期化していれば、引数無しのコンストラクタ。いわゆる既定のコンストラクタを省略してもよいです。呼び出さないから、いらないということです。派生クラスのコンストラクタに初期化リストがない場合はコンストラクタは既定のコンストラクタを呼び出すということです。


 この項目はそんなところでしょうか?まだ説明が足りないコンストラクタの機能があれば追記します。思い出せオレ。


C++に戻る