こんにちはぷないしんです。
タイトルに深い意味はありません。
C++の大きな特徴であるクラス、今日はその継承について今一度振り返ってみます。
という事でよく説明や試験問題で使われる”会員”クラスを作っていき、例によってそれらを解説、改良していきながらやっていきます。
ではまずヘッダーから
Member.h
#ifndef ___Member
#define ___Member
class Member{
std::string full_name;
int number;
double weight;
public:
//コンストラクタ
Member(const std::string& name, int no, double 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 string& name, int no, double 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に体重の設定を任せています。
また、氏名の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", 1, 48);
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";
}
ここではパンピーのキムラ君を表しているのが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& name, int no, double w, const 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 string& name, int no, double w, const string& spe)
: 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(){
double weight = Kusanagi.get_weight(); //クサナギの体重
Kusanagi.set_weight(weight - 1.5); //クサナギが減量(-1.5kg)
<< "Special is " << Kusanagi.get_special() <<"\n";
}
はい
VIP会員のクサナギタケシ君を表しているのが、クラスVIPMember0型のオブジェクトであるKusanagiです。
会員番号は3番で、体重は60kgからダイエットに成功し58.5kg、特典は一本サティスファクションです。
さて、この一般会員クラスとVIPクラスを両方を使うプログラムを書いてみます。
Wtest.cpp
#include <iostream>
#include "Member.h"
#include "VIPMember0.h"
using namespace std;
void diet(Member& m, double dw){
double weight = m.get_weight();
if (weight > dw) m.set_weight(weight - dw);
}
//VIPダイエット(体重をdw減らす)
void diet(VIPMember0& m, double dw){
double weight = m.get_weight();
if (weight > dw) m.set_weight(weight - dw);
}
int main(){
Member Kimura("Kimura Takuma", 1, 48);
diet(Kimura,0.3); //キムラが0.3kgダイエット
cout << "No:" << Kimura.no() << " " <<Kimura.name() << " is " << Kimura.get_weight() << "kg\n";
<< "Special is " << Kusanagi.get_special() <<"\n";
}
二つのdiet関数は会員のダイエット処理を行う関数です。
同じ処理をしている関数が各クラス毎に作られ、多重定義をしています。
なぜかというと、関数の処理を行う対象の変数の”型”が違うからです。
(この関数の違いは、引数"m"の型だけで、その他は全部同じ)
一般クラスとVIPクラスの見た目は殆ど同じなのですが、コンパイラ視点で見ると、何の関係もないクラスです。
同一、または似たようなクラスを仕様の違うクラスとして書いてしまうと、
プログラム上のあちらこちらに”似て非なる”クラスが大量に存在し、開発効率、保守性が下がります。
そしてこの話を継承して明日へ…
ぷないしん。