BK通信 - C++ のバッドノウハウ

最終更新日: 2008-07-08

WEB+DB PRESS Vol. 46 に向けて書いた記事の元の原稿です。


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

C++ 再考

いまどきのウェブアプリケーションは Perl や Ruby などの lightweight 言語で作られているものが多く、 C++ の出番は非常に少ないようにみえます。

ところが、知人などに話を聞いてみると、基本的に Perl で作られているサービスでも、性能に効いてくる要所要所に C++ が使われているという話をよく耳にします。

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

意外と長生きするやつだぜ
意外と長生きするやつだぜ

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

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

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

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 と指定した関数は基本的に外から呼べなくなります。が、以下のようなマクロで簡単にアクセス制御を骨抜きにできると知りました。

#define private public

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

ただし、上のマクロだけだと、下のようなコードには対応できないので、 #define class struct というマクロで、class を強制的に struct に置き換えてしまうと、より強力です。

class Foo {
  int func();
  ...

class のメンバはデフォルトで private であるのに対し、 struct のメンバはデフォルトで public であるためです。

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

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

隠されているからこそ見たい!
隠されているからこそ見たい!

C++ と goto

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

 
  while (...) {
    while (...) {
      if (...) {
        goto done;
      }
    }
  }
done:
  printf("おわったよ")

では次のように、ループの中に飛び込むコードはどうでしょうか。

while (...) {
  while (...) {
    // A
    ...
  deep_inside:
    ...
  }
}
goto deep_inside;

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

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

そんなとこ飛び込むんじゃねーよ
そんなとこ飛び込むんじゃねーよ

まとめ

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

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

C++のことにも先達はあらまほしき事なり
C++のことにも先達はあらまほしき事なり


*1 オブジェクトをバイト列表現などに変換すること
*2厳密には non-POD (Plain Old Data) 型のオブジェクト
*3<http://www.amazon.co.jp/exec/obidos/ASIN/4894714515/>
*4<http://www.amazon.co.jp/exec/obidos/ASIN/4894716860/>