第44章 テンポラリ

 前回、クラスのオブジェクトを直接返すコードを書きました。今回は、こういう場合どういう動作になるのかなどについて話したいと思います。


 では、今回の要点です。


 では、いってみましょう。


 前回の operator- をもう一度見てみましょう。

fraction operator-(int nNum, fraction frcSub)
{
    fraction ret(frcSub.m_nDenom * nNum - frcSub.m_nNumer, frcSub.m_nDenom);
    return ret;
}

 赤いところに注目して下さい。fraction クラスのオブジェクトを直接返しています。しかし、ret の寿命はこの関数内なのに、こんなことをしてもいいのでしょうか?

 しかし、よく考えてみて下さい。戻り値の型は参照でもポインタでもないので、その心配はないはずです。int 型の内部変数を返すときも問題はありませんね。

 これは実際にそうで、引数で値渡しを行ったときと同じようにオブジェクトのコピーが返されます。ret の寿命はこの関数内で終わりですが、コピーを返すのでその点は問題ないというわけです。値渡しならぬ値返しなわけです。

 しかし、ここで疑問が生じます。このオブジェクトのコピーの寿命はどうなるのでしょうか?

 それを考えるには、実際にこの関数の使われる状況を考えてみるといいでしょう。これは例えば

fraction a(1, 2);
fraction b;

b = 3 - a;

という風に使われるわけです。3 - a で返された値(オブジェクト)は b に代入されます。代入作業は値のコピーになるので、もう 3 - a から返されたコピーは以後不要になるわけです。

 つまり、b = 3 - a; という文が終わってしまえば、戻り値は捨ててしまっても構わないということです。これは実際にそうなっていて、戻り値がオブジェクトの場合、その寿命はその文が終わるまでです。

 このように、戻り値の寿命は非常に短いものです。このオブジェクトは、戻り値のために一時的に作られたオブジェクトということで、テンポラリオブジェクトと呼ばれます(「テンポラリ」とは「一時的な」という意味です)。


 テンポラリオブジェクトというのは、戻り値以外にも作ることができます。先ず、次のプログラムを見て下さい。

プログラム
// TempObj1.cpp
#include <iostream.h>

class fraction
{
private:
    int m_nNumer;
    int m_nDenom;

public:
    fraction(int nNumer = 0, int nDenom = 1)
        { m_nNumer = nNumer; m_nDenom = nDenom; }
    void Disp() const
        { cout << m_nNumer << '/' << m_nDenom << endl; }
    friend fraction operator-(int num, const fraction& frcSub);
};

fraction operator-(int num, const fraction& frcSub)
{
    return fraction(num * frcSub.m_nDenom - frcSub.m_nNumer, frcSub.m_nDenom);
}

int main()
{
    fraction a;

    a = 2 - fraction(1, 2);
    a.Disp();

    return 0;
}
実行結果
3/2

 赤くなっているところを見て下さい。何と、fraction のコンストラクタを呼んでいます。こうすると、テンポラリオブジェクトが作られます。

 先ず、上の方です。fraction(num * frcSub.m_nDenom - frcSub.m_nNumer, frcSub.m_nDenom) を return しています。こうすると、この引数でコンストラクトされたオブジェクトを一時的に作って、それを return することになります。やっていることは fraction ret; を作っていたときと同じですが、もっとコンパクトに書くことができますね。

 このテンポラリオブジェクトの寿命も前と同じで、return 文が終わると破棄されてしまいます。この時には既に戻り値のコピーは終わっているので、何の問題も起きることはありません。

 次に、下の方です。2 - fraction(1, 2) を行っています。fraction(1, 2) は 1/2 に相当するオブジェクトを一時的に作ります。これを使ってoperator- を呼んで、2 - 1/2 を行います。答は 3/2 ですね。この関数内で 3/2 に値するテンポラリオブジェクトが作られ、戻り値でテンポラリオブジェクトが作られ、そして a に代入が行われます。これだけで合計3つのテンポラリオブジェクトが作られていることになりますね。

 そして、1/2 のオブジェクトは a = 1 - fraction(1, 2); の文が終わると破棄されます。operator- の戻り値も同じタイミングで破棄されますね。この時の破棄の順番は、後に作られた方が先です。つまり、戻り値の方が先に破棄されるというわけです。あまり意識することはないかもしれませんが、一応知っておくといいかもしれませんね。


 それでは、今回の要点です。


 最後に補足しておくと、VC++だと、テンポラリオブジェクトを参照に代入すると、寿命が参照の寿命にまで引き延ばされるようです。しかし、参照に代入できること自体がVC++固有の機能のようなので、一般的に言えることではないと思われます。

 しかし、この拡張機能を切っても operator- の const fraction& にテンポラリオブジェクトを代入できます。この参照を返すことで死んだオブジェクトへの参照を持つことも可能です(この時に返されるのは参照なので、上のVC++固有云々の話とは状況が違います)。テンポラリオブジェクトの寿命には充分気を付けて下さい。

 それでは、次回まで。


第43章 心の友よ! | 第45章 えっ!?

Last update was done on 2001.2.21

この講座の著作権はロベールが保有しています