引き続き継承の話

既存のクラスを基にして新しいクラスを作ることを,クラスの継承 (inheritance) と呼ぶ。
継承の話をするために、(良い設計ではないが)円を表すクラスと、円柱を表すクラスを考える。

円を表すクラス

名前
cBase
継承の話をしたいのでついベースクラスって意味で命名してしまったが
ふつうは、当然作るオブジェクトに即した名前を付ける
メンバ変数
変数名 x, y ⇒ 中心座標(整数) 非公開
変数名 r  ⇒ 半径(実数) 公開
変数名 area ⇒ 面積(実数) 非公開
メンバ関数
cBase() ⇒ デフォルトコンストラクタ(デフォルトコンストラクタは引数なしコンストラクタのこと)
cBase(int _x, int _y, double _r) ⇒ (引数付きコンストラクタ)
void PrintMember() ⇒ 自身のメンバを表示する関数
void SetArea() ⇒ 半径をもとに免責を計算してareaにセットする関数
double GetArea() ⇒ areaを返す関数
Fig. 1: cBaseクラスの詳細

実際のソースコード

"cBase.h"
//実はインデントは2日4つの半角スペースが標準派がおおい。
//今後参考にしていくgoogleのコード規約でもスペース
 
class cBase
{
int x, y; //メンバ変数
public: //↓ここからのメンバはパブリック(公開)
	cBase() //コンストラクタは2つオーバーロードしてある
		:x(0), y(0), r(0.0), area(0) {
		cout << "生誕" << endl;
	} //メンバイニシャライザによる初期化
 
	cBase(int _x, int _y, double _r)
		:x(_x), y(_y), r(_r),area(0) {//areaは0を入れておいて後で計算
		cout << "生誕" << endl;
		SetArea();//areaを計算
	} 
 
	~cBase() { cout << "消滅" << endl; } //デストラクタ
	double r;//メンバ変数
	void PrintMember(); //メンバ関数(公開)宣言のみ
	double GetArea() { return(area); }
private://↓ここからのメンバは再びプライベート(非公開)
	double area;//メンバ変数
	void SetArea(); //面積を計算してareaにセットする関数 宣言のみ
};
"cBase.cpp"
#include "cBase.h"
 
void cBase::PrintMember()//メンバ関数(公開)
{
	cout << "中心: (" << x << "," << y << ")" << endl;
	cout << "半径:" << r << endl;
	cout << "面積:" << area << endl;
}
 
void cBase::SetArea() //面積を計算してareaにセットする
{
	const double pi = 3.14159265359;
	area = pi * r * r;
}
 
// cBase:: の中ではpublic,private区別

使っていくー

"theMain.cpp
#include <iostream>
#include "cBase.h"
#include "cCylinder.h"
 
using std::cout;
using std::cin;
using std::endl;
 
int main()
{       //特にコンストラクタを指定しないと引数なしコンストラクタが呼ばれる
	cBase inVar; //クラスからできた変数=インスタンス=C++でのオブジェクト
	cBase inVar2(10, 5, 3.0);
 
	inVar.PrintMember();
	inVar2.PrintMember();
 
	return(0);
}
Fig. 2: 実行結果

cBaseクラスまとめ

クラスを設計するには、そのクラスをモデル化(抽象化)することが大事です。
簡単には、
そのオブジェクトを表すのに必要なパラメータ ⇒ メンバ変数
そのオブジェクトのふるまいを表すもの ⇒ メンバ関数
になります。

cBaseクラスの場合

継承してみる

次は、先ほどのcBaseクラスを継承して、cCylinderクラスを作ってみます。
説明のために無理やり作ったクラスなので設計としてはよくありません。(むしろダメ)
余裕のある人はこの設計のどこがダメなのか考えてみよう!
hint: 前に言った、基底クラスと派生クラスの関係は、どんなかんけいになってるのがよかったっけ?

作るクラスは以下のようなクラスにします。

円柱は、底面と上面に円があり、側面を壁にした形状です。

考え方としては、形状(円)を拡張して(高さと体積の属性をプラス)して円柱クラスを作る。
です。

名前
cCylinder
基底クラス
cBase
メンバ変数
変数名 height 高さ(実数) 非公開
変数名 volume 体積(実数) 非公開
メンバ関数
cCylinder() ⇒ デフォルトコンストラクタ
cCylinder(int _x, int _y, double _r, double height) ⇒ (引数付きコンストラクタ)
void SetVolume() ⇒ 半径と高さをもとに体積を計算してvolumeにセットする関数

コードを書いていくぅ

"cCylinder.h"
#include "cBase.h"
class cCylinder :
    public cBase
{
    double height;
    double volume;
    void SetVolume();
public:
    cCylinder()
        :cBase(),height(0),volume(0)
    { cout << "円柱爆誕" << endl; }
    cCylinder(int _x, int _y, double _r, double _h)
        :cBase(_x, _y, _r), height(_h), volume(0) //Volumeは0にして後で計算
    {
        cout << "円柱爆誕" << endl;
        SetVolume(); //Volumeを計算してセット!
    }
    //void PrintMember(); //メンバ関数(公開)宣言のみ
};
"cCylinder.cpp"
#include "cCylinder.h"
 
void cCylinder::SetVolume()
{
	//volume = area * height; //これはできない! areaはベースのプライベートメンバだから
	volume = GetArea() * height;
}
"theMain.cpp"
int main()
{
	cCylinder inCy(10, 5, 3.0, 5.0);
	//中心(10,5) 半径 3.0 高さ5.0の円柱
	inCy.PrintMember();
 
	return(0);
}

ってな感じになります。

* 実行結果

PrintMember関数が、cBaseクラスに実装されているので、新しく追加したメンバを表示できていませんね(上面と底面の円の属性を表示しちゃってる)。
次はこれをどうしたらcCylinderクラスのメンバ詳細を表示できるのかってのを考えてみます。