C++ class とそれに関係してくるものを書いて覚えるための初心者自己中記事⑤
の続き
意図としては派生クラスを宣言時に基底クラスのコンストラクタが動いた段階で
SetBase関数 ( 純粋仮想関数 ) を呼び出して当該派生クラスのSetBase関数を呼び出させようとした。
---------------------------------------------------
//Input class //抽象クラス
Input::Input(double n){
SetBase(); //純粋仮想関数をかくとエラー
inputNum = n;
}
double Input::Get(){
return inputNum;
}
bool Input::Set(){
SetBase();
return inputNum > 0;
}
---------------------------------------------------
エラーになる。
これはコンストラクタの呼ばれる順番が関係していて、基底クラスのコンストラクタが呼び出されている時点では派生クラスのコンストラクタはまだ呼び出されておらず、
基底クラスのコンストラクタ内で純粋仮想関数を呼び出すと、実装されてないよ!ってエラーになる。派生クラスのSerBase関数はこの段階では認識されていない?のだ。
なので今回の意図を達成するためにはそれぞれ派生クラスのコンストラクタ内の処理で行う必要がある。
前回記述したコンストラクタ・デストラクタの順番に追加すると。
基底クラスのコンストラクタ(基底クラスで使用できるメンバのみ)
派生クラスのコンストラクタ
派生クラスのデストラクタ
基底クラスのデストラクタ (基底クラスで使用できるメンバのみ)
なのだ。
次、
スコープ解決演算子
InputCin classではSetBase関数内で cin を使って入力した値を inputNum に代入していました。
次にInputCin class を継承したHalfInputCin class を作成します。
このクラスではSetBase 関数内で親クラスのSetBase 関数を呼び出し、その後inputNumに代入された値を半分にする処理を書きます。
その際HalfInputCin class 内でInputCin class の関数を呼び出しす方法が
スコープ解決演算子です。
使い方は簡単で、変数の呼び出し部分で先頭に 当該クラス名::
をつけるだけです。
---------------------------------------------------
// InputCin class の派生クラス
class HalfInputCin : public InputCin{
protected:
virtual void SetBase();
};
~~~~~~~~~~~~~~~~~~~~~~
//HalfInputCin class (InputCin class の派生クラス)
void HalfInputCin::SetBase(){
InputCin::SetBase();
inputNum /= 2;
}
---------------------------------------------------
これで呼び出せます。
あと、前回の勉強でやった仮想関数で基底クラスから派生クラスの当該メンバ関数を呼び出す流れはデストラクタでも同様。
とりあえずクラスは一旦終わった。
最後に出来上がった(テキストまま)のをメモ
---------------------------------------------------
// Input class 基底クラス
class Input{
public:
Input(); //コンストラクタ
virtual ~Input() = 0;
double Get(); //共通点
bool Set(); // Set関数内ではSetBase関数を呼び出す その後0以上かをチェックする
protected:
virtual void SetBase() = 0; //Set関数から値の設定部分を抜き出した SetBase関数が仮想関数になるので各派生クラスもそれに準じる
double inputNum; //共通点
};
// InputCin vlass 派生クラス
class InputCin : public Input{
public:
InputCin();
virtual void SetBase();
private:
};
// InputArray class 派生クラス
class InputArray : public Input{ // class InputArrayは配列の管理
public:
InputArray(const double* array, int size); //配列を受け取る引数付きコンストラクタ
virtual ~InputArray();
virtual void SetBase(); //Set関数が呼び出されるたびにinputIndexとinputNumに次のインデックス番号と数値が記憶される
private:
double* inputArray; //配列のアドレスポインタ
int inputIndex; //Set関数で現在のインデックスを記憶
};
~~~~~~~~~~~~~~~~~~~~~~
//Input class
Input::Input(){
}
double Input::Get(){
return inputNum;
}
bool Input::Set(){
SetBase();
return inputNum > 0;
}
//InputCin class
InputCin::InputCin(){
}
void InputCin::SetBase(){
cin >> inputNum;
}
//InputArray class
InputArray::InputArray(const double* array, int size){
inputArray = new double[size];
copy(array,array + size, inputArray);
inputIndex = 0;
}
InputArray::~InputArray(){
delete[ ] inputArray;
}
void InputArray::SetBase(){
inputNum = inputArray[inputIndex];
++inputIndex;
}
~~~~~~~~~~~~~~~~~~~~~~
//Average関数
void Average(Input& input){
int count = 0;
double avr = 0.0;
while(input.Set()){
avr += input.Get();
++count;
}
avr /= count;
cout << avr << endl;
}
void Show(Input& input){ //基底クラスで派生クラスのオブジェクトを参照する
input.Set();
cout << input.Get() << endl; //派生クラスを参照している基底クラスがGet関数を使う
}
Input* CreateInput(){
int ch;
cout << "1.InpotCin 2.InputArray" << endl;
cin >> ch;
switch(ch){
case 1:
return new InputCin;
break;
case 2:
static const double array[ ] = {1.1,2.2,3.3,4.4,-1,};
return new InputArray(array,5);
break;
default:
return 0;
}
}
int main(){
Input* input = CreateInput();
Average(*input);
delete input;
}
---------------------------------------------------
ここまで