構造化プログラミングとオブジェクト指向と

これまで、C++のごくごく基礎的なところを学んできました。
C++はC言語の仕様を「ほぼ」まるっと含んでしまっているので、C++のいいところだけを使ってC言語を拡張しただけの形でソースコードを記述することができます。
このように、C言語の構造化プログラミング手法をそのまま使って、C++の機能の使いやすいものだけを使って書く手法を「better C」と呼んだりします。
これまでやってきたものが、だいたいbetter Cの状態です。
これまでの手法でも、わりとC++のおいしいところはいただけたのですが、今後はもっとC++の高度な機能を学んでいこうではないかと思います。

オブジェクト指向(Object oriented Programing)

オブジェクト指向は、これまで処理ベースで関数として仕事を切り分けていくというプログラムの組み立て方をしてきたのですが、これを「対象(オブジェクト)の性質を抽象化」することでプログラムを組み立てていく手法になります。(=難しくて意味わかりません)

違いを見るために、とりあえず今までの手法をまとめてみましょう。

構造化プログラミング手法

これまでは、問題が与えられたとき以下のように仕事を切り分けていました。
1.問題が与えられる
2.必要な変数を考える
3.仕事をまとまった単位に分けて細分化する(サブルーチン化)
4.細分化された仕事ごとに、処理をまとめる
5.処理を関数化する
6.必要な場面で、必要な関数を呼び出し全体の処理の流れを完成させる。

実際の処理の例

じゃあ、ってことで、実際に処理をまとめて関数で処理していく例を見てみましょう。 問題は以下のようになっています。

あなたは、とある学校の教務(学生の成績などを処理する係)担当の先生です。 それで、あなたは業務として、N人所属の学級の成績の処理を任されました。 学級の各学生は、それぞれ何科目かの試験を受けていて、成績のデータとして以下の数値が必要です。

各学生について、

学級について


さて、どのように処理していきましょうか?

データと処理をピックアップする


学生数 N人:
科目数 sNum教科:

学級について

          ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
さらに実際に数値が入った例を考えてみる。
学生数5人の学級で、3科目の試験を行った場合のプログラムを考えてみる。
すなわち

点数は以下のようになったらしいです。

Tab. 1: 各学生の点数
No 氏名 国語 理科 社会
01 山田 80 50 88
02 佐藤 75 75 75
03 鈴木 80 80 85
04 後藤 60 60 55
05 佐々木 100 65 50

変数の割り当て

実際に処理に使われる変数を割り当てていきます。

string name[N] = {"yamada","satou","suzuki","gotou","sasaki"}; 
//5(=N)人学級のメンバー名 山田、佐藤、鈴木、後藤、佐々木がクラスのメンバー
string sname[sNum] = {"国語","理科","社会"};
//3(=sNum)科目の各科目名

次に点数関係

//N人の学生分のsNum教科文の得点の配列
int point[N][sNum] = {{80,50,88},//yamadaの得点
	              {75,75,75},//satouの得点
	              {80,80,85},//suzukiの得点
	              {60,60,55},//gotouの得点
	              {100,65,50}//sasakiの得点
	              };//sNum 0:国語 1:理科 2:社会
float totalAverage; //全体の全科目の平均点
float student_ave[N]; //各学生ごとの全教科の平均点(sum(各教科点)/3)x学生数分N
float subject_ave[sNum]; //各教科ごとの平均点

2次元配列覚えてる?

関数を作って、割り当てた変数に目的の結果を代入していく

次は、仕事を関数としてまとめていきます。
C言語のプログラマの仕事は主に、変数を渡されたときに、指示された通りに動く関数を設計することです。
(関数が難しいとか言ってる人は、プログラマにはなれないよ!)
もう一回問題を確認します。

あなたは、とある学校の教務(学生の成績などを処理する係)担当の先生です。 それで、あなたは業務として、N人所属の学級の成績の処理を任されました。 学級の各学生は、それぞれ何科目かの試験を受けていて、成績のデータとして以下の数値が必要です。

各学生について、

  • 名前
  • 各科目の点数
  • 各学生の全科目に対する平均点

学級について

  • 全体の全科目に対する平均点
  • 全学生の科目ごとの平均点(国語の平均点、数学の平均点・・・)

ここで、求めなければならないものをまとめてみます。
データとして以下のものがすでに用意されているとします。

各学生について、

  • 名前
  • 各科目の点数

その時に、

各学生について、

  • 各学生の全科目に対する平均点

学級について

  • 全体の全科目に対する平均点
  • 全学生の科目ごとの平均点(国語の平均点、数学の平均点・・・)

を求める。
というのが、今回の処理になります。
これらを求めるために少なくとも3つの関数を作らなければならなそうです。

  1. 各学生に対して学生の全科目に対する平均点 を求める関数
  2. 学級全体に対して、全体の全科目に対する平均点 を求める関数
  3. 学級全体に対して、科目ごとの平均点 を求める関数

1つずつ考えていきます。

各学生に対して学生の全科目に対する平均点 を求める関数(設計) 

今回のデータでは、学級内の5名の各学生は、3教科の試験を受けています。
各学生の3科目の平均点を求めることになります。
求めた各学生の平均点は、

float student_ave[N];
//各学生ごとの全教科の平均点(sum(各教科点)/3)x学生数分N\\

に代入します。(そのために用意した変数だもの)
各学生の全教科の平均点を求める関数なので、関数名は、

calcStudentAverage
//各学生に対し教科の平均点を計算する関数

ぐらいにしましょう。
そのほか各学生の教科平均の計算に必要なものは、


ぐらいかと思います。
まとめると

  1. calcStudentAverageは全学生の全点数のデータをスキャンして、
  2. 学生数N人分の教科平均((国語+理科+社会)/sNum )を計算して、
  3. float student_ave[N]に代入する関数である。

と言えます。

なので、最終的に関数の設計としては以下のようになります。

//戻り値 無し
//第1引数 学生の点数のデータを表す配列(の先頭アドレス)
//第2引数 学生数
//第3引数 教科数
//第4引数 平均点の入れ物
//プロトタイプ宣言:
void calcStudentAverage(int _p[N][sNum], int _n, int _snum, float _ave[N]);
各学生に対して学生の全科目に対する平均点 を求める関数(実装) 

さっき設計した、calcStudentAverageを実装していきます。

//戻り値 無し
//第1引数 学生の点数のデータを表す配列(の先頭アドレス)
//第2引数 学生数
//第3引数 教科数
//第4引数 平均点の入れ物
//プロトタイプ宣言:
void calcStudentAverage(int _p[N][sNum], int _n, int _snum, float _ave[N]);

やることは、

  1. 平均点の初期化
  2. 全科目の平均の計算(人数分)

です。
引数の役割はそれぞれ、


したがって、


平均点の初期化

	for(int i=0;i<N;i++)
		_ave[i] = 0.0;

平均点の計算

	for(int i=0;i<N;i++)
	{
		_ave[i] = (_p[i][0] + _p[i][1] + _p[i][2]) / 3.0;
	}

全部まとめると。

void calcStudentAverage(int _p[N][sNum], int _n, int _snum, float _ave[N])
{
	for(int i=0; i<N; i++)
		_ave[i] = 0.0;
	for(int i=0; i<N; i++)
	{
		_ave[i] = (_p[i][0] + _p[i][1] + _p[i][2]) / 3.0;
	}
}

学級全体に対して、全体の全科目に対する平均点を求める関数(設計) 

同様に、学級の人数分の全科目の平均を求める関数を作っていきます。

void calcSubjectAverage(int _p[N][sNum],int _n, int _snum,float _ave[sNum]); 第1引数 学生の名前を表す配列 第2引数 学生の平均点データを表す配列 第3引数 学生の数 == 学級全体に対して、全体の全科目に対する平均点を求める関数(実装) == == 学級全体に対して、全体の全科目に対する平均点を求める関数(設計) == == 学級全体に対して、全体の全科目に対する平均点を求める関数(実装) == あいあいあ
still under construction… === 2次元配列覚えてますか? === == 2次元配列の宣言 == 基本的には1次元配列と同じですが、行と列の要素数を書かなければならない
(宣言文) 型名 配列名[行の要素数][列の要素数]; (例)
3行x4列のint型の2次元配列

  int a[3][4];
  
2次元配列の初期化

 1次元配列と同様,初期化子を{}で囲み,先頭から順にカンマで区切る。

   //宣言文
   型名 配列名[行要素数][列要素数] = {1, 値2, 値3, ...
  };

1.初期化 基本的な書き方
 初期化リストを次元に対応させて{}で区切ります。

(例1int a[2][3] = {
                   {1,2,3},
                   {4,5,6}
                    };
 
(例2int a[2][3] = {
                   {1,2,3}, // 行と列の対応と明確に...
                   {4,5,6}
                   };

2.要素数が足りない場合
 初期化子の足りない要素は0で初期化されます。ただし{}の囲み方によって初期化の仕方が異なるので注意が必要です。

(例)
    int a[2][3] = {1,2}             // {1,2,0,0,0,0}になる
    int a[2][3] = {{1},{4,5}}      // {{1,0,0},{4,5,0}}になる


今回のやつ

N人の学生、sNum教科分の得点が必要です。
N=5、sNum=3の時なら 5×3=5行x3列の配列が必要になりそうです。
宣言と同時に初期化するパターンで書くと、以下のようになりそう。

//N人の学生分のsNum教科分の得点の配列
int point[N][sNum] = {{80,50,88},//yamadaの得点
	              {75,75,75},//satouの得点
	              {80,80,85},//suzukiの得点
	              {60,60,55},//gotouの得点
	              {100,65,50}//sasakiの得点
	              };
//sNum 0:国語 1:理科 2:社会

under construction