C++によるプログラミング入門7
クラスの練習

 こんにちは。はやいもので、もう、5月ですね。今回は「クラスを書く練習」です。

 昔、「2次方程式の解の公式」なんてありましたね。数学の先生があれを黒板で導いたのを見て、すぐ「なるほど、わかった」と、思った人もいるでしょうが、普通の人(私なんかです)は、それより、例題を解いていてなんとなくわかったような気がしたのではないでしょうか?えーと、数学の話なんかしてすみません。
 つまり、わかろうと考え込むより、やってみる方がわかるのでは、という話です。プログラムの場合、「コンパイル・実行」->「改造」->「コンパイル・実行」->「改造」->・・・を繰り返すのがよいと思うのです。はじめは単純なタイプミスでコンパイルエラーがでますが、そのうち、タイプミスはすぐに探せるようになります。そうこうしているうちに、おっかなびっくりでなく(簡単な)プログラムが作れるようになってきます。そしてわかったような気がしてくるから、不思議です。


 クラスの書き方はわかりましたか?まだ、しっくりこないかもしれませんね。今回は、復習しながら単純なクラスを作ってみます。その過程で、少し新しいことも説明しましょう。
 もう一度前回の分を読み直して...なんていいですから、今回の分を最後のプログラムのところまでさっと読んでみてください。それから、前回の分を読み直すのもよし、今回の分を精読するのもよしです。お勧めは、やっぱり、「プログラムをコンパイル・実行してみる」です。^^)

 さて、どんなクラスを作ってみましょうか?と、考えるのもたのしいですね。今日は「ロケット」をつくってみます。ロケットの持つべき「データ」(つまり、メンバ変数データメンバ)と「動作・機能」(つまりメンバ関数)は何でしょうか?私なら、次のように考えます。

    クラス ロケット
    データメンバ:燃料
           現在の速度
    メンバ関数 :コンストラクタ
           加速

 まあ、はじめはこんなもんでしょう。ロケットに名前をつけたい、発射の仕掛けもつくりたい、など、要望は多々あるでしょうが、はじめは簡単にします。
 前回書いたように、基本的に、メンバ変数はprivate、メンバ関数はpublicとします。そして、燃料と速度は、簡単に整数としましょう。するとintを使えばいいですね。それぞれnenryo、sokudoとします。

 それから、コンストラクタを考えましょう。どんなクラスでもコンストラクタを書かなければいけないということではありません。しかし、たいていの場合は必要になります。ということで、ここでもコンストラクタを書きます。(コンストラクタを書かない場合については、次回に説明します。)
 コンストラクタで書くことは、基本的に、「生成されるオブジェクトにどのようなデータを与えるか」が中心です。「猫」のときは名前を与えました。このようにメンバ変数の値を決めてやるのがコンストラクタの主な仕事なのです。これをオブジェクトの(データの)初期化なんて言います。「オブジェクト」はメモリ上の「もの」(名前のついた具体的な「猫」や「ロケット」)という意味でした。「初期化」とは一般に「データなどをはじめに決めてやる」という意味なのです。さあ、どう初期化しましょうか。たとえば、燃料を引数で決めてやって、速度を0にすればいいでしょう。
(「コンストラクタ」も「引数」も初心者にはわかりにくいですね。上に書いたように「コンストラクタ」はオブジェクトを生成するときに使うものです。「引数」はコンストラクタなど関数のカッコの中にいれるデータのことでした。ここで考え込まずに下の使用例を見てください。)
 加速のメンバ関数はどうしましょうか。加速は物理学の法則によると、、、なんてやめましょう。加速一回で速度が適当に(私は2とします。もちろん深い理由はないので別の数でもかまいません)上がるようにしましょう。そのとき、当然燃料が減るはずですね。同じ数字(つまり2)だけ燃料が減るようにしましょう。以上をまとめると、ロケットのクラスRocketは次のようになります。

class Rocket       //スペルをまちがえるとかなり恥ずかしいので辞書を見ましょう
{
    int nenryo; 
   //燃料
    int sokudo; 
   //現在の速度
public:
    Rocket(int);  
//コンストラクタの宣言
    void kasoku(); 
//加速を表す関数の宣言
};

//コンストラクタの定義
//nenryoに引数として与えられる値(xで表す)を入れ、sokudoに0を入れているだけです
Rocket::Rocket(int x) : nenryo(x), sokudo(0){}

//加速を表す関数の定義ですが、なんだこりゃ。これも下の説明参照。
void Rocket::kasoku(){
    sokudo += 2;
    nenryo -= 2;
}

 いくつか新しいこととまだなじんでいないことがでてきたと思いますが、安心してください。ゆっくり説明します。
 まず、クラス定義の中身を見てください。メンバ変数nenryoとsokudoを定義し、メンバ関数(コンストラクタとkasoku)を宣言しています。メンバ関数は定義を外に書くつもりなので、宣言だけにしました。
 コンストラクタの定義(クラス定義の外)では、「nenryo(x), sokudo(0)」で、nenryoとsokudoを「xの値」と0に初期化しています。前回も説明しましたが、このようにメンバ変数の初期化ができるのです。覚えていなければ、ここでまた覚えればよいと思います。もちろん、他の選択も可能ですが、今回はこれでいきましょう。
 ところで、関数(コンストラクタもそうでない関数も)が「外から受け取るデータ」を引数というのでした。コンストラクタの定義の「Rocket::Rocket(int x)」の「int x」は、「引数を受け取ったらそれをxで表す」という意味です。(正確には、「引数の値をxにコピーする」です。)このように、関数の定義の中で、引数を表す変数を仮引数と言ったり、パラメータと言います。xは仮引数とよばれるものなのです。
 なお、クラス定義の中のコンストラクタの宣言には、仮引数を書かずに「Rocket(int);」としました。宣言部分では、どうせ定義もないので、仮引数を書かなくてもよいのです。これを「Rocket(int x);」と書いてもかまいません。好みの問題だと思います。私個人は、旧C++入門を書いていた頃は、宣言部でも仮引数を書いていたのですが、最近は書かないようになりました。もちろん、みなさんは、お好きなように。

 加速を表すメンバ関数kasoku()の前のvoidは、「何も報告しないで終わる」という程度の意味でした。(どこへ報告するのかは今は気にしないでください。いやですね、こういう言い方は。でもしかたがなかったなと、すぐに納得がいくはずです。)その定義では、今回はじめて説明するものを使っています。つまり、

sokudo += 2; 
nenryo -= 2;

です。これは、変な式ですが、それぞれ「sokudoに2を足せ」「nenryoから2を引け」という意味になるのです。「+=」「-=」は、それぞれ「右辺のものを左辺に足せ」「右辺のものを左辺から引け」という意味になると、そう決めてあるからです。(つまり、そういうものだと、ここでおぼえてください。)

 実はこれらは

sokudo = sokudo + 2;
nenryo = nenryo - 2;

と書くこともできます。数式としては変ですが、これは数式ではありません。C++では、「=」は代入を表すのです。したがって、上の式は、「sokudoにsokudo + 2を代入せよ」つまり「sokudoに2を足せ」、「nenryoにnenryo - 2を代入せよ」つまり「nenryoから2を引け」という意味になるのです。このような書き方より、「+=」「-=」を使う方が一般的ですが、「sokudo = sokudo + 2;」なども理解しておくと、あとで役に立つはずです。
 ところでこのロケットは変です。燃料があってもなくても加速できるロケットですよね。これでは未来から来たネコ型ロボットのようです。燃料が切れたら、加速不可能になるように、kasoku()を書き直した方がよさそうです。(注:すみません。最近知ったのですが、あのスーパーロボットもちゃんとメンテナンスが必要なんですね。)
 そのようなときには、if文というものを使うのですが、まあ、答えを見てから考えましょう。

class Rocket         //改良版ロケット
{
    int nenryo;      //燃料
    int sokudo;      //現在の速度
public:
    Rocket(int x);   //コンストラクタ
    void kasoku();   //加速を表す関数
};

//コンストラクタの定義
Rocket::Rocket(int x) : nenryo(x), sokudo(0){}

//加速を表す関数の定義
void Rocket::kasoku()
{
    if(nenryo >= 2){
        sokudo += 2;
        nenryo -= 2;
        cout<< "現在の燃料は" << nenryo << "です。" << endl;
        cout<< "現在の速度は" << sokudo << "です。" << endl;
    }
    else{
        cout << "燃料切れです。加速できません。漂流です。" <<endl;
    }
}

 kasoku()の中身は2つのブロックにわかれています。と、言っても最初はわからないでしょう。ifのブロック(ifの横の{から3行下の}まで)とelseのブロック(elseの横の{から2行下の}まで)です。ifのすぐあとの丸カッコ内の式「nenryo >= 2」は条件を表しています。ここは

  もし、nenryoが2以上ならば、

などと読みます。この「>=」はイコールが下についた不等号のかわりです。(高校生が「大きいかまたは等しい」とかなんとかと読むやつです。)nenryoは一回の加速に2使うので、それが2以上なければ、もう燃料切れで、加速できないと考え、そのチェックをしているのです。
 そして、もしnenryoが2以上なら、はじめに考えたようにsokudoに2を足し、nenryoから2を引きます。また、ついでにその時の燃料と速度を表示するようにしました。それがifのブロックに書いてあることです。
 elseは

  そうでなければ、

などと読みます。つまり、ここでは「nenryoが2以上でなければ」という意味ですね。その場合は、燃料切れという意味ですから、ロケットは加速せず漂流としました。それがelseのブロックに書いてあることです。
 if文の一般的な形は、1番下にまとめますが、とりあえず、先に進みましょう。

 それでは、このRocketクラスを使った、またまた簡単なプログラムを書いてみます。

//RocketSample.cpp
#include <iostream>
using namespace std;

class Rocket         //改良版ロケット
{
    int nenryo;      //燃料
    int sokudo;      //現在の速度
public:
    Rocket(int x);   //コンストラクタ
    void kasoku();   //加速を表す関数
};

//コンストラクタの定義
Rocket::Rocket(int x) : nenryo(x), sokudo(0){}

//加速を表す関数の定義
void Rocket::kasoku()
{
    if(nenryo >= 2){
        sokudo += 2;
        nenryo -= 2;
        cout<< "現在の燃料は" << nenryo << "です。" << endl;
        cout<< "現在の速度は" << sokudo << "です。" << endl;
    }
    else{
        cout << "燃料切れです。加速できません。漂流です。" <<endl;
    }
}

int main()
{
    cout << "ロケットをメモリ上につくります。燃料(整数)を入力してください。"<<endl;
    int n;       //ユーザの入力した値を格納する変数をひとつ用意しました。その名はnです。
    cin >> n;    //ユーザの入力した数字をnに代入しました。

    //nをコンストラクタに渡してロケットをつくります。
    Rocket ohtori(n); //これでnの値がコンストラクタの定義のxにコピーされ、
                      //コンストラクタの中身が実行され、nenryoの値がnの値に
                      //なったロケットohtori(鳳号)が生成されるわけです。
                      //このohtoriをオブジェクトなどとよぶのでした。
    cout<< "加速します。" <<endl;
    ohtori.kasoku();  //ohtoriに対してkasokuを使っています
    cout<< "また、加速します。" <<endl;
    ohtori.kasoku();  //ohtoriに対してkasokuを使っています
    cout<< "またまた、加速してみます。" <<endl;
    ohtori.kasoku();  //ohtoriに対してkasokuを使っています
    cout<< "鳳号の冒険は終わりました。" <<endl;
}

Fig.1 RocketSample.exeの実行画面1(燃料をはじめに30にした)

Fig.2 RocketSamle.exeの実行画面2(燃料をはじめに4にした)

 解説はあまりいらないと思うのですがどうでしょうか。mainの中では、Rocketクラスのオブジェクトohtoriを作り、それを3回加速させているだけです。Rocketクラスの使い方、つまり「オブジェクトの生成とメンバ関数の使い方」を見ておいてください。
 一言コメントをしておきます。ohtoriは、「Rocketの変数」ということができますが、C++では、「クラス型の変数」は「オブジェクトそのもの」を意味すると考えることができるのです。(関係ありませんが、Javaなどでは、変数とオブジェクトを区別する必要があるのでした。Javaプログラマの人は注意してください。)
 鳳号(ohtori)は3回加速していますね。もし、燃料切れにならなければ、速度が増えて、、、それだけです。もし、はじめの燃料がたりなければ、つまり、5以下なら、途中で漂流するでしょう。う〜ん。プログラムとしてはあんまりおもしろくないですね。(それでも、コンピュータが思いどうり動いてくれて、楽しいとは思うのですが、、、。)でも、まあ基礎ですから。基礎は飽きてきたという人、もう少し待ってください。(注:子供の頃に見た「妖星ゴラス」という映画はとても印象的でした。その映画に出てきたかっこいいロケットが鳳号という名前でした。深い意味はないのですが。)


 

 最後に、if文のまとめを書いておきます。ざっとながめておいて、必要になったときに、見るようにしてもよいでしょう。

if文とは、一般に、

if(条件a){
    処理A
}
else if(条件b){
    処理B
}
  ...
else{
    処理Z
}

のような形をしていて、、条件aが正しいとき処理Aが実行され、そうではなくて条件bが正しいときは処理Bが実行され、...となり、どの条件も正しくないときは、処理Zが実行される、というものです。ただし、ここで、「else if(...){...}」はいくつあっても(なくても)かまわないし、また、「else{...}」の部分は、なくてもかまいません。

 また、条件は次のように書きます。

aとbに対して、条件の書き方

a == b  aとbが等しい(とき)
a != b  aとbが等しくない(とき)
a < b   aがbより小さい(とき)
a <= b  aがbと等しいかbより小さい(とき)
a > b   aがbより大きい(とき)
a >= b  aがbと等しいかbより大きい(とき)


目次のページ
前のページ 次のページ