多くのプログラミングに備わっているブロック文({}
)の意外な活用法や応用テクニックを紹介します。
目次
- トランザクション系の処理で活用
- メモリやオブジェクトの開放・破棄処理で活用
- 同じ変数名を複数回使いたい場合に活用
- 一時変数の有効範囲を制限したい場合に活用
- プリプロセッサとの連携テクニック
- コメント文との連携テクニック
トランザクション系の処理で活用
処理の開始位置と終了位置を明確にする事ができます。
int main() {
TransactionBegin();
{
Execute("create table company(id integer, name text);");
Execute("insert into company values(1, 'DAISO');");
Execute("insert into company values(2, 'SHOP99');");
}
TransactionEnd();
}
メモリやオブジェクトの開放・破棄処理で活用
ポインタや参照の使用範囲を明示したり制限することができます。
int main() {
{ // とあるオブジェクトの生存域を明確化する
File f = new File();
f.close();
}
f.close(); // ERROR
{ // メモリ管理にも応用可能
void *p = malloc(8);
that(p);
free(p);
}
that(p); // ERROR
}
このようにオブジェクトやポインタの有効範囲を明確にできる点が本イディオムの特徴です。ブロック内の変数は他のブロックからはアクセスできなくなるため、二重開放などのうっかりミスを防げるというメリットが得られます。またこの記法により、コードがより構造的に読みやすくなり、機能の役割も明確になるという利点が生まれます。
ちなみにブロック文の開始括弧にコメントや機能名を記述すると更に便利です。あまりオススメはしませんが、ラベルを活用することもできます。
int main() {
{ // DEKAPAN //
puts("hoe");
}
label_a: {
puts("hell");
}
{ label_b:
puts("wolrd");
}
}
コード整形ツールによってはlabel_a: {}
の例のように、インデントが一段下の階層へと矯正されることがあります。この性質を利用することでコードの横幅を小さく抑えることもできます。
同じ変数名を複数回使いたい場合に活用
異なるブロック間であれば、変数名が重複しても名前衝突が発生しません。
int main() {
{
int value = 9;
printf("%d", value);
}
{
int value = 8; // 異なるブロックであるため重複可
printf("%d", value);
}
}
よく使われる変数名の再利用が可能となります。また似たような処理をブロック文で明示することも有効です。
Alert alert = new Alert();
{
Button button = new Button();
button.title = "Yes";
alert.addButton(button);
}
{
Button button = new Button();
button.title = "No";
alert.addButton(button);
}
alert.show("Marry me?");
一時変数の有効範囲を制限したい場合に活用
コード内で宣言した一時変数が、別の異なる行やブロック内で誤って利用されないようにすることができます。
{
int a = 1; // 一時変数
printf("%d", a);
}
int b = 2;
printf("%d", a); // エラー: 誤って使用されることもない
変数の有効範囲を明確化することは、誤ったコードの記述を回避できるだけでなく、コードの読みやすさを高める効果もあります。上記の例では変数a
は、ブロックの外では利用されないということが保証されるため、ブロックの外ではその変数aのことを考える必要が無くなります。結果として、ブロックの外では余計な変数aのことを忘れて、より少ないワーキングメモリで効率的にコードを読み進めることができます。
似たようなテクニックとして、末尾アンダースコアによる命名を活用する方法も考えられます。
Number value_ = this.long_long_name();
int value = value_.intValue();
末尾アンダースコアが用いられた一時変数は文脈内やコードブロック内でのみ使われるということを暗黙的に示唆します。ブロック文が活用できないようなケースで利用すると良いでしょう。
もっとも、このような暗黙的なルールやセマンティクスは、厳格さを重んじるプログラマには忌避される傾向にあるため、より保守的な姿勢を示すべく、明確な命名や明確なセマンティクスを用いるのも良いでしょう。
// 代替案1
Number value_ref = that.method();
int value = value_ref.intValue();
// 代替案2
int value;
{
Number ref = that.method();
value = ref.intValue();
}
// 代替案3(intをプリミティブ型と仮定)
int value = that.method()
.intValue();
プリプロセッサとの連携テクニック
else文の処理{ bar(); }
は、非デバッグビルド時には通常のブロック文として認識されます。
#ifdef DEBUG
if (cond) {
foo();
}
else
#endif
{
bar();
}
DEBUG
マクロ未定義時にはcond
の状態にかかわらず、常にbar
関数が実行されます。
コメント文との連携テクニック
テキストエディタによっては、波括弧をダブルクリックすると波括弧内の文字列が選択状態になる機能が備わっている場合があります。これを活用することで、複文の一括選択が可能となります。
// ↓ この開始括弧をダブルクリックすると
// {
puts("hello");
puts("world");
// }
// テキストエディタ側でこのような選択状態になる場合がある
// {
puts("hello");
puts("world");
// }