C++によるプログラミング入門12
値を戻す関数

 こんにちは。今日は、「値を戻す関数」あるいは「関数の戻す値」についての話をします。この話は、はやくしなければと思いつつなかなかできなかった話です。
 C++などでは、一般に何かするもののことを関数と言うのでした。(そのほかにマクロというものもあるのですが、ここでは触れません。)そして、関数は一般に「値を戻す」のです。(「戻す」を「返す」ということも多いようです。)...と言われても、何のことかわからないですね。わからないのが正常です。ゆっくりいきましょう。
 実は、関数を使うと、普通の関数はその「場所」に何かを残していく(あるいは、報告していく)のです。その残していくものを「関数の戻り値」などというのです。では、どのような値を戻すかというと、それは関数の作者が決める値なのです。
 また、値を戻さない関数もあります。今まで我々がつくった関数はすべて値を戻さない関数だったのです。そして、「値を戻さない」と宣言しているのが、関数の前のvoidだったのです。
 入門5のプログラムneko.cppをもう一度みてみましょう。コンストラクタの前には何もついていません。それはコンストラクタが特別な関数であるため、そういう約束になっているからです。関数naku()の前にはvoidがあります。気になっていた人も多いでしょうね。これが、この関数が何も戻さない(あるいは「報告しない」)という意味の宣言だったのです。voidとは「無」という意味ですね。
 今回は、このような「値を戻さない関数」ではなく「値を戻す関数」を紹介します。

 ところで、「値を戻す」ということは、クラスのメンバ関数でない関数でも同じです。説明を簡単にするため,まず、そのような関数を考えることにしましょう。下のプログラムをみてください。

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

//関数func()を定義します。
void func()
{
    cout << "こんにちは。私はコンピュータです。" << endl;
}

int main()
{
    func();   //funcを使っている。
}

 少し見慣れない形式ですね。上のプログラムでは、まず関数func()を定義しているのです。関数と言ってもただ「こんにちは。私はコンピュータです。」と画面に表示するだけの関数です。このようにクラスとは何の関係もない関数を定義することもできるのです。このような関数を一般に、グローバル関数などといいます。
 そして、mainの中に「func();」と書いてありますが、ここで関数funcを実行しているのです。(つまり、こう書くと、関数funcを実行できるわけです。)ただし、ここではvoidを書きません。というのは、voidがいるのは関数を宣言したり定義するときだけだからです。mainのなかにある「func();」は関数func使っているだけで、宣言や定義ではないため、voidはいらないのです。念のため。
 上のプログラムを、実行して確かめてみてください。これは、funcを実行するだけ、つまり、「こんにちは。私はコンピュータです。」と画面に表示するだけのプログラムなのです。

Fig.1 ex1.exe

 どうしてこんなめんどうなことをするのだろうと思いましたか?「cout << "こんにちは。私はコンピュータです。" << endl;」を直接mainの中に書けば終わりなのにと思った人、あなたのプログラムの能力は着実に向上しています。しかし、もっと複雑なプログラムの場合、このように関数を定義することは重要です。なぜでしょう。それは、「関数もクラスのようにプログラムの部品となり得るからだ」と考えた人、あなたはこの講義をパーフェクトに理解しています。あまりに簡単な例なのでピンとこないかもしれませんが、そういうわけなのです。

 さて、ここで、上のプログラムを次のように変えてみます。

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

//関数func()を定義します。
int func()
{
    cout << "こんにちは。私はコンピュータです。" << endl;
    return 1;   //整数値1を戻す
}

int main()
{
    func();     //func()を使っている。
}

 上のプログラムを実行すると、結果ははじめのものと全く同じになります。しかし、プログラムの意味は少し違うのです。まず、funcの定義の前がvoidではなくintであることに注意してください。これは「intすなわち、整数を戻す関数である」という意味です。どこに戻すかというとこの関数を呼び出した「場所」に戻すのです。そして、この関数の定義の一番下の行にある「return 1;」が実際に戻す値を決めているのです。これは、「数値1を戻す」という意味です。(注:戻すというと「借りたものを戻す」という感じにとれますが、ここではそうではありません。誰もどこからも何も借りていませんので。ただ、「呼び出した場所に整数を置いてくる」というような意味で「戻す」という言葉を使っているのです。これはreturnの日本語訳です。はじめに書いたように、「返す」という人もいますが意味は同じです。)
 では、この戻された値1はプログラム中でどのようにつかわれているでしょうか?実は、全く使われていません。main内でfuncが呼ばれているので「cout << "こんにちは。私はコンピュータです。" << endl;」はちゃんと実行されるのですが、戻された値は何の役も果たしていないのです。
 もし、戻された値をちゃんと表示したければ、たとえば、次のようにmainを書き直せばよいのです。

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

//関数func()を定義します。
int func()
{
    cout<<"こんにちは。私はコンピュータです。"<<endl;
    return 1;   //整数値1を戻す
}

int main()
{
    int d;      //整数変数dの宣言(「整数dを使うよ」という意味)    
    d = func(); //考え込むと不思議な文ですが、次のような意味になります。
                //まず、func()の中身が実行される。
               
//そして、この関数から戻された値1がdに代入される。
    cout << "func()から戻された値=" << d << endl;  //dの値を出力
}

 ここで、「d = func();」はなんとも不思議な文ですが、これで、func()の中身がまず実行され、次に値1が戻され、それがdに代入されることになるのです。ここでいう「戻される」とは、実質的には、この関数func()が実行されたあと、func()と書いてあるところが数値1に変わるということなのです。
 実際実行してみてください。まず、画面に「こんにちは。私はコンピュータです。」と出力され、その後に(と言っても,人間の目にその順序は見えませんが)数値1が画面に出力されて終わりになります。

Fig.2 ex3.exe

 ex2.cppとex3.cppの違いは、単に、戻された値を使うか使わないかの違いです。このように戻された値を使う使わないはプログラマの自由なのです。

 上の例はあまりにも無意味な例だったので、「二つの整数値をきいて、その合計を戻す」関数をつくって、その関数を使うプログラムを書いてみましょう。

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

//足し算をする関数
int add()
{

    int a, b;  //二つの整数をいれる変数
    cout << "これから二つの整数の足し算をします。" << endl;
    cout << "まず、ひとつめの整数を入力してください。" << endl;
    cin >> a;
    cout << "もうひとつの整数を入力してください。" << endl;
    cin >> b;
    //次のreturnで合計を戻す
    return a + b;
}

int main()
{
    int d = add(); //整数変数dが宣言され、すぐにadd()が実行され、その戻す値がdに代入される。
    cout << "合計:" << d << endl;
}

Fig.3 tasizan.exe

 この例のように、戻り値は定数である必要はないのです。


 それでは、初心者には少し難しい問題をひとつ。できなくても大丈夫です。とても熱心な人は今までの復習をしながら考えてみてください。それほど熱心でもない普通の人は、ちょっと考えてから、答えを眺めてみてください。

問題:
入門5の猫を次のように改良したクラスをつくってください。

  クラス 猫
    データメンバ:名前
           体力(整数)       
    メンバ関数 :コンストラクタ(名前を受け取り、はじめの体力を10とする)
           食事をもらう(もらってたべた量だけ体力を増やす関数)  
           鳴く(鳴いて、体力を5減らす)       
 ただし、関数「食事」と「鳴く」は実行後の体力を「戻り値」で報告するものとします。

解答例:
class Neko
{
    string name;  //名前
    int tairyoku; //体力
public:
    Neko(string n) : name(n), tairyoku(10){}
    int syokuji();  //食事を(ユーザから)もらう関数
    int naku();     //鳴く関数
};

int Neko::syokuji(){
    cout << name << "に食事をさせます。どれだけ食べさせますか?" << endl;
    cout << "半角の数字で入力してください。" << endl;
    int food;          //食べさせる量 ユーザに決めてもらう
    cin >> food;
    tairyoku += food; 
    return tairyoku;   //tairyokuの値を戻す
}

int Neko::naku()
{
    cout << "にゃあ。俺様は" << name << "だ。" << endl; 
    tairyoku -= 5; 
    return tairyoku;   //tairyokuの値を戻す
}

 「tairyoku += food;」は「tairyokuにfoodを足せ」、「tairyoku -= 5;」は「tairyokuから5引け」という意味でしたね。ポイントはコンストラクタを除く関数の前にintがあり、それらの定義の最後にはreturn文があるということです。

 このクラスを使ったプログラム例は次のようなものでしょう。

//neko4.cpp
#include <iostream>
#include <string>  //stringを使うために必要
using namespace std;

class Neko
{
    string name;  //名前
    int tairyoku; //体力
public:
    Neko(string n) : name(n), tairyoku(10){}
    int syokuji();  //食事を(ユーザから)もらう関数
    int naku();     //鳴く関数
};

int Neko::syokuji(){
    cout << name << "に食事をさせます。どれだけ食べさせますか?" << endl;
    cout << "半角の数字で入力してください。" << endl;
    int food;          //食べさせる量 ユーザに決めてもらう
    cin >> food;
    tairyoku += food; 
    return tairyoku;   //tairyokuの値を戻す
}

int Neko::naku()
{
    cout << "にゃあ。俺様は" << name << "だ。" << endl; 
    tairyoku -= 5; 
    return tairyoku;   //tairyokuの値を戻す
}

int main()
{
    cout << "猫をメモリ上に生成します。名前を決めてください。" << endl;
    string temp;
    cin >> temp;    
    Neko kai_neko(temp);  //飼い猫の生成 名はユーザがtempに入力したもの
    cout << endl;
    for(int i = 0; i < 5 ;i++){  //5回だけメモリ上の猫と遊ぶプログラムです
        cout << "どうします?" << endl;  
        cout << "1 食事を与える 2 鳴かす" << endl;
        cout << "半角の数字で入力してください。" << endl;
        int ans;
        cin >> ans;
        if(ans == 1){
            int t;
            t = kai_neko.syokuji();  //まず、Neko::syokujiの内容が実行され、その結果の体力が
            //Neko::syokujiの戻り値として与えられ、tに代入される。
            cout << "猫の体力=" << t << endl;
        }
        else if(ans == 2){ 
            int t;  //上のtとは別の{ }ブロック内にあるので全く無関係
            t = kai_neko.naku();  //まず、Neko::nakuの内容が実行され、その結果の体力が
            //Neko::nakuの戻り値として与えられ、tに代入される。
            cout << "猫の体力=" << t << endl;
            if(t < 0){
                cout << "食事が不十分だったので、猫は隣のお金持ちの家に行ってしまいました。"<<endl;
                break;  //for文からぬけろという命令 体力が負になったらおしまいということです
            }    //if(t<0)のかっこ閉じる
        }        //if(ans==2)のかっこ閉じる
        cout << endl; //見やすさのための改行
    }            //for(i=0;i<5;i++)のかっこ閉じる
    cout<<"おわり"<<endl;
}

 できれば、このくらいのプログラムをなんとか読んで理解できるようにしてください。(きびしいでしょうか?)自分で書けなくても読んで理解できるというところが今日の目標です。

Fig.4 neko4.exe


 ところで、気が付いていましたか?mainもよく見ると、関数の定義と同じ形をしていますね。

int main()
{
    ...

 実は、mainは、プログラムの中心を表わすグローバル関数なのです。そして、mainの前にintを書くのは、この関数はintを戻すように、決められているからなのです。
 そして、一般に(慣習的に)、mainは、プログラムが正常終了したところで0を戻すことになっています。そのため、mainの最後に「return 0;」と書くのが「正式」なのです。ただし、C++では、mainの最後に何も書かなければ、自動的にそこに「return 0;」があることになるのです。つまり、いつも書くことなので、省略してもよいことになっているのです。そのため、この入門では、mainの最後に「return 0;」を書いていないのです。しかし、それにしても、「mainはintを戻す関数」ということはおぼえておきましょう。


 戻り値にはいろいろなものがあるのですが、今回はこの辺にしておきましょう。
 このような値を戻す関数は,たとえば、前の「魔王とヒーローの対決」で、魔王とヒーローの対決を複数回にするときなどにも使えます。
次回にゆっくりやってみましょうか。
目次のページ
前のページ  次のページ