クラスでケガしたけど軽傷ですんだ

こんにちはぷないしんです。

 

タイトルに深い意味はありません。

 

C++の大きな特徴であるクラス、今日はその継承について今一度振り返ってみます。

 

という事でよく説明や試験問題で使われる”会員”クラスを作っていき、例によってそれらを解説、改良していきながらやっていきます。

 

ではまずヘッダーから

Member.h

//パンピークラス

#ifndef ___Member
#define ___Member

class Member{
    std::string full_name;
    int     number;
    double  weight;

public:
    //コンストラクタ
    Member(const std::string& nameint nodouble w);
    //名前
    std::string name()const{
        return full_name;
    }
    //番号
    int no() const {
        return number;
    }
    //体重返却
    double get_weight() const {
        return weight;
    }
    //体重設定
    void set_weight(double w){
        weight = (W > 0) ? w : 0;
    }
    //情報表示
    void print() const;
#endif

 

 このクラスのデータメンバは下の三つです・

・名前”full_name

・会員番号"number"

・体重"weight"

 

コンストラクタは【name】【no】【w】に受け取った三つの値でそれぞれのメンバを初期化します。

コンストラクタの他、5つのメンバ関数が定義されています。

それぞれ

 

名前を取得する【name】 会員番号を取得する【no】体重を取得する【get_weight】、また設定を行う【set_weight】、会員情報を表示する【print

ですね。

 

つづいてこちらを

Member.cpp

#include <iostream>
#include "Member.h"

using namespace std;

//コンストラクタ
Member::Member(const stringnameint nodouble w)
                : full_name(name),number(no){
                    set_weight(w);
                }

//情報表示
void Member::print() const{
    cout << "No" << number << ":" << full_name << "(" << weight << "kg)\n";
}

 体重(メンバのweight)を設定するメンバ関数のset_weightはweightがマイナスにならないように調整(もしwにマイナスを受けとった場合、weightに0を入れる)

コンストラクタの

set_weight(w);

部分はset_weightに体重の設定を任せています。

 

また、氏名のfull_nameと番号のnumberの初期化はコンストラクタの初期化子で行っています。↓

 full_name(name),number(no)

 

では、Member型のオブジェクトを一つ作り、各ゲッターを呼び出すだけの簡単なものを書いていきます。

 Membertest.cpp

#include <iostream>
#include "Member.h"

using namespace std;

int main(){
    Member kimura("Kimura Takuma"148);

    double weight = kimura.get_weight();    //キムラの体重
    kimura.set_weight(weight - 1.5);        //キムラが減量(-1.5kg)

    cout << "No:" << kimura.no() << " " <<kimura.name() << " is " << kimura.get_weight() << "kg\n";
}

 

f:id:punainen:20210325143002p:plain

実行結果

ここではパンピーのキムラ君を表しているのがMember型のオブジェクトkimuraです。

キムラ君の会員番号は1で、体重は48kg、ところが、キムラ君は1.5kgのダイエットを成功したのでget_weightとset_weightを使い、体重を46.5kgに更新してます。

VIP会員クラス

さて、このクラブは特典を付けた【VIP会員】制度が出来ました。

会員ごとに内容が違う特典をstring型のメンバで表す【VIPクラス】を作ります。

パンピークラスMemberを元に、VIPクラスを作るのは簡単ですね。

ヘッダーとソースの各ファイルをコピーし、部分的な追加と変更をするだけです。

この方法で作った”試作版”のVIPクラスのヘッダーとソース部を作ります。

 

VIPMember0.h

//VIPクラス

#ifndef ___VIPMember0
#define ___VIPMember0

#include <string>

class VIPMember0{
    std::string full_name;
    int     number;
    double  weight;
    std::string special;

public:
    //コンストラクタ
    VIPMember0(const std::string& nameint nodouble wconst std::string& spe);
    //名前
    std::string name()const{
        return full_name;
    }
    //番号
    int no() const {
        return number;
    }
    //体重返却
    double get_weight() const {
        return weight;
    }
    //体重設定
    void set_weight(double w){
        weight = (w > 0) ? w : 0;
    }
    //情報表示
    void print() const;
    //特典
    std::string get_special() const {return special;}
    //特典設定
    void set_special(const std::string& spe){
        special = (spe != "") ? spe : "Unregistered";
    }
};

#endif

 特典のstring型のデータメンバspecialを作ると同時に、特典の取得と設定をするget_special、set_specialを追加しました

specialのセッターset_specialは、もし仮引数に空文字を受け取ったとき、文字列"Unregistered"をspecialに代入するようにしています)

 

それとMember.cppの方も手を入れていきましょう。

 

VIPMember0.cpp

#include <iostream>
#include <string>
#include "VIPMember0.h"

using namespace std;

//コンストラクタ
VIPMember0::VIPMember0(const stringnameint nodouble wconst stringspe)
                : full_name(name),number(no){
                    set_weight(w);
                    set_special(spe);
                }

//情報表示
void VIPMember0::print() const{
    cout << "No" << number << ":" << full_name << "(" << weight << "kg)\n"
    << "Special =" << special << "\n";
}
 

 コンストラクタの仕様も変えています

特典用の文字を受け取る仮引数【spe】が増えると同時に、その値の設定を処理するところを追加しています。

(メンバ【special】の値設定はメンバ関数の【set_special】に任せているので、仮引数【spe】に空文字を受け取った時、【special】には"Unregistered"が表示される)

 

そして、これらを利用する実装部です。

 

VIPMember0test.cpp

 

#include <iostream>
#include "VIPMember0.h"

using namespace std;

int main(){
    VIPMember0 Kusanagi("Kusanagi Takeshi"360"Ippon satisfaction");

    double weight = Kusanagi.get_weight();    //クサナギの体重
    Kusanagi.set_weight(weight - 1.5);        //クサナギが減量(-1.5kg)

    cout << "No:" << Kusanagi.no() << " " <<Kusanagi.name() << " is " << Kusanagi.get_weight() << "kg\n"
    << "Special is " << Kusanagi.get_special() <<"\n";
    }

 

f:id:punainen:20210326154346p:plain

実行結果

はい

VIP会員のクサナギタケシ君を表しているのが、クラスVIPMember0型のオブジェクトであるKusanagiです。

会員番号は3番で、体重は60kgからダイエットに成功し58.5kg、特典は一本サティスファクションです。

 

さて、この一般会員クラスとVIPクラスを両方を使うプログラムを書いてみます。

Wtest.cpp

#include <iostream>
#include "Member.h"
#include "VIPMember0.h"

using namespace std;

//パンピーダイエット(体重をdw減らす)
void diet(Member& mdouble dw){
    double weight = m.get_weight();
    if (weight > dwm.set_weight(weight - dw);
}
//VIPダイエット(体重をdw減らす)
void diet(VIPMember0& mdouble dw){
    double weight = m.get_weight();
    if (weight > dwm.set_weight(weight - dw);
}
int main(){
    Member Kimura("Kimura Takuma"148);
    VIPMember0 Kusanagi("Kusanagi Takeshi"360"Ippon satisfaction");

    diet(Kimura,0.3);       //キムラが0.3kgダイエット
     cout << "No:" << Kimura.no() << " " <<Kimura.name() << " is " << Kimura.get_weight() << "kg\n";

    diet(Kusanagi1.2);    //クサナギが1.2kgダイエット
    cout << "No:" << Kusanagi.no() << " " <<Kusanagi.name() << " is " << Kusanagi.get_weight() << "kg\n"
    << "Special is " << Kusanagi.get_special() <<"\n";
    }

 二つのdiet関数は会員のダイエット処理を行う関数です。

同じ処理をしている関数が各クラス毎に作られ、多重定義をしています。

なぜかというと、関数の処理を行う対象の変数の”型”が違うからです。

(この関数の違いは、引数"m"の型だけで、その他は全部同じ)

 

一般クラスとVIPクラスの見た目は殆ど同じなのですが、コンパイラ視点で見ると、何の関係もないクラスです。

同一、または似たようなクラスを仕様の違うクラスとして書いてしまうと、

プログラム上のあちらこちらに”似て非なる”クラスが大量に存在し、開発効率、保守性が下がります。

 

そしてこの話を継承して明日へ…

 

ぷないしん。