一時オブジェクトの生存期間

Home > 2006-01 / 実装技術 > This Entry [com : 0][Tb : 0]

2006-01-18

前回に引き続き、今回も一時オブジェクトの話題を取り上げます。C言語では、一時オブジェクトといっても、せいぜいスタックやレジスタが割かれる程度のことでしたが、C++では、一時オブジェクトの生成・解体に伴ってコンストラクタやデストラクタが呼び出されます。これによって、予期しないボトルネックが発生したり、期待通りの動作にならなかったりするわけです。

さて、今回は一時オブジェクトの生存期間(寿命)についてです。一時オブジェクトの生存期間は、一部の例外を除いて*1、完結式の評価の最後までということになっています。ただし、標準化以前の古いC++では、ブロックの最後に解体されていたため、現在でもそのようなコンパイラが残っている可能性があります。

完結式の評価の最後に一時オブジェクトは解体されますので、次のようなコードは動作が未定義になってしまいます。

std::string str("abc");
const char* s;

s = (str + "de").c_str();
std::puts(s);

文字列の連結("abc"と"de"をつなげて"abcde"を作る)の段階で一時オブジェクトが生成されており、s はその一時オブジェクトと生死を共にする配列の先頭要素を指しています。そして、puts を呼び出す時点では、一時オブジェクトは既に解体されていますから、s の値は無効になっているわけです。しかし、

std::puts((str + "de").c_str());

とすることは可能です。puts の呼び出しが完了するまで、一時オブジェクトは解体されないからです。

一時オブジェクトの生存期間を把握していると、それを期待した実装を行うことも可能になります。たとえば、排他制御を行う lock クラスについて考えてみましょう。lock クラスはコンストラクタで割り込みを禁止し、デストラクタで元に戻す仕様だと考えてください。通常であれば、

{
  lock x;
  ++global_variable;
}

のように、明示的にブロックを作って生存期間をコントロールしようとします。しかし、for 文の式などでアトミックなインクリメントを実現しようとすると、このようなやり方ではいったん関数にせざるを得なくなります。ところが、一時オブジェクトの生存期間の性質を知っていれば、次のような書き方が可能になります。

for (volatile int* p = &gloval_variable; /* 条件 */; lock(), ++*p)
{
  /* 処理 */
}

アトミックに処理させようとする式の直前に lock(), を書くだけで、簡単に排他制御を行うことができるのです。コンマ演算子の各オペランドの評価直後は副作用完了点にはなりますが、完結式の終わりではないので、この時点では一時オブジェクトは解体されません。副作用完了点と完結式の最後は混同しやすい概念であるため、注意が必要です。


*1 JIS X3014:2003 12.2 一時オブジェクトを参照

Comment

Post a Comment









管理者にだけ表示を許可

Trackback

http://cppemb.blog17.fc2.com/tb.php/47-83d2c602

C++と組み込み環境 | Page Top▲

New >>
関連ブログ開設のお知らせ
<< old
左辺値のように振舞う一時オブジェクト
ブログ内検索
RSSフィード