BK通信 ―Bad Knowhow Tsushin―

#03C++のバッドノウハウ

ソフトウェアなどを使いこなすために、ストレスを感じながらもしぶしぶ覚えなければならないようなノウハウ、⁠バッドノウハウ」がテーマの本連載、第3回の今回は極めてBKフル(バッドノウハウが多い)言語であるC++を取り上げたいと思います。

C++再考

いまどきのWebアプリケーションはPerlやRubyなどのLightweight Languageで作られているものが多く、C++の出番は非常に少ないように見えます。ところが、知人などに話を聞いてみると、基本的にPerlで作られているサービスでも、性能に効いてくる要所要所にC++が使われているという話をよく耳にします。

C言語ではなくC++を使うのは、オブジェクト指向言語ということもありますが、標準ライブラリに含まれる便利なコンテナ(string、vector、mapなど)を使いたいという理由も大きいようです。たしかに、一度これらのコンテナを使ってしまったら、C言語には戻れなさそうです。

それでは、本題のBKの話に入りましょう。

C++のboolと未初期化変数

C++にはboolという型があり、true(真⁠⁠、false(偽)の値をとります。と、思っていたのですが、初期化を忘れているとtrueでもfalseでもない値になることがありますリスト1⁠。

リスト1 初期化を忘れたら
bool b; // 初期化を忘れている
if (b == true) {
  printf("trueです\n");
} else if (b == false) {
  printf("falseです\n");
} else {
  printf("どっちでもないよ\n");
}

このくらいの簡単な例ならコンパイラの警告で簡単に検出されますが、もう少し複雑になると警告が出なくなります。boolに限らず未初期化の変数は厄介なバグの元ですが、trueかfalseだと思い込んでいると、さらに見つけにくいバグになりそうです。

C++のアクセス制御

C++はクラスのメンバに対して、private、protected、publicというアクセス制御をかけることができます。たとえば、privateと指定した関数は基本的に外から呼べなくなります。が、リスト2のようなマクロで簡単にアクセス制御を骨抜きにできると知りました。

リスト2 アクセス制御を骨抜きにするマクロ
#define private public

リスト2のマクロは、すべてのprivateという識別子をpublicに置き換えるという意味です。このマクロを定義してからヘッダを読み込めば、privateだったフィールドがすべてpublicになり、じゃんじゃんアクセスできるようになります。

ただし、リスト2のマクロだけだとリスト3のようなコードには対応できないので、 リスト4のようなマクロでclassを強制的にstructに置き換えてしまうと、より強力です。classのメンバはデフォルトでprivateであるのに対し、structのメンバはデフォルトでpublicであるためです。

リスト3 こんなコードの場合には?
class Foo {
  int func();
  ...
リスト4 classを強制的に
#define class struct

このようなテクニックが必要な場面はほとんどないと思いますが、デバッグやテストのためにどうしてもprivateな関数を呼びたいというときに、もしかしたら役立つかもしれません。

...という話を知人にしたところ、オブジェクトのシリアライズ[1]にこのテクニックが使われているのを見たことがある、とのこと。結局、そのコードは別のやり方で書き直すことになったそうですが。

C++とgoto

プログラミング言語におけるgoto文は歴史的に、使っていい派(大域的な脱出に便利)と使ってはいけない派(コードがごちゃごちゃになる)による議論があります。それはさておき、C++にもC言語から受け継いだgoto文があります。goto文を使いたいときは、おもに多重のループから外に出たい場面でしょうリスト5⁠。

リスト5 多重ループから外に出たい
while (...) {
  while (...) {
    if (...) {
      goto done;
    }
  }
}
done:
  printf("おわったよ")

ではリスト6のように、ループの中に飛び込むコードはどうでしょうか。リスト6のコードがC++の言語仕様的に正しいかどうかは、Aの部分のコードの中身によります。というのも、Aの部分オブジェクト[2]が作られている場合、いきなりdeep_insideに飛び込んだらオブジェクトを正しく作ることができません。というわけで、C++の言語仕様ではこのようなgotoは許していません。

リスト6 ループに飛び込みたい
while (...) {
  while (...) {
    // A
    ...
  deep_inside:
    ...
  }
}
goto deep_inside;

普通はこんなことはやらないと思いますが、ふと気になったので調べてみたら、言語仕様でしっかり言及されていて感心した次第です。

まとめ

今回はC++のバッドノウハウを3つ紹介しました。最初のboolの話は実際にはまった問題ですが、残りの2つは興味本位で調べてなるほどと感心したBKです。たまには何の役にも立たないBKを深追いしてみるのも、いい気分転換になります(ただの現実逃避という気も...⁠⁠。

ところで、C++の落とし穴、有用なバッドノウハウは『Effective C++ 原著第3版』注3⁠、⁠C++ Coding Standards』注4などの多くの文献でカバーされています。これからC++を始めてみたいという人は、まずこれらの書籍に目を通すことをお勧めします。先人の知恵を利用して、できるだけ落とし穴を避けていきましょう。

おすすめ記事

記事・ニュース一覧