2010年4月12日月曜日

C/C++言語の反省

色々なエントリにバラバラに書いちゃったので
一旦纏めます。

21世紀現在に至っても、
世の中の手続き型プログラミング言語は、
どこかしらC/C++に似た部分があります。

言い換えると、C/C++言語の反省点を踏まえて
言語設計をしていると言えます。

C/C++言語の、筆者の考える反省点は次のとおりです。

  • boolean型が無い。
  • 関数の戻り値として、1個しか返せない。
  • 生ポインタ。
  • 故にメモリ管理が手動
  • (template)
  • プリプロセッサ
  • #include
  • (例外)
  • (名前空間)
  • (多重継承)
そもそもの話、C言語自体設計が古いので、
現状のメモリ事情やら、ソフトウエア技術やらに即して居ません。

象徴的なのが、「きめ細かいメモリ管理」を「人間がやる」ところ。

メモリが狭いって言ったって、携帯電話でもメガの単位はあります。
キロバイト単位を節約して、8ビットCPUを絞り出していた時代とは違います。
その証拠に、イマドキのケータイアプリは、概ねJavaです。

ポインタはC/C++の歴史的魔道と言えます。
メモリ管理方法が極めて少ないので、多重ポインタで実装するしかありませんが。
その中のどこかに構造体ポインタが挟まってたらもう駄目です。
この宣言がどエラい苦労します。
もはや呪いのアイテムです。

Cの時代は、メモリ確保と、その用法が完全に無関係なので、
何バイト確保しようが、それをいくらでも逸脱できます。
故に、バッファオーバーラン等の危険が常に付きまといます。
気をつけます、って言ったって、限界があるでしょうそりゃあ。

C++では、一応class宣言があって、new/deleteが有りますが、
deleteが手動だったりしますし。

プリプロセッサは、
プログラムソースを、コンパイラに入れるまえに書き換える。
コンパイラの機能があまりにも少ない故の、魔道具です。

「構造体宣言の前半の中括弧まで」を展開するマクロとか
ソースとマクロ定義のどっちを見ても、気持ち悪そうな実装が出来ます。

「関数の宣言に、特定のナニかを付け加えるマクロ」なんかは、
オープンソース業界の、軽量言語の実装では今でも散見します。

#includeは、
関数定義を、外部ファイルからサラリと読み込む方法がないので、
ソースプログラムを書いたら、*.c/cppと*.hの一組を書くのが「お約束」で、
「ウチの関数を使いたかったら#includeしてね?」って事になってますが、
その*.hは、利用のためだけじゃなくて、本体の実装のためでもあるので、
あちこち#includeを渡り歩いています。

これがまた面倒くさい。
多重include防止の為の #ifdef とか、もう生活の知恵の極致です。

#includeの向こう側が書き換わっちゃってると、
関数の引数とか、構造体の形が変わっちゃってるかもしれないので、
全てリビルドせざるを得ません。

そのためのmakeやらmakefileやらはもちろん或る訳ですが、
仕事を増やすための仕事に絶望した!

関数の戻り値が、1個しか戻せないのは、結構あちこちでハマリます。
構造体をnewしてreturnするとかは、C++ではあまりやりたくないので
1個だけreturnして、残りは引数に、参照渡しで入れ物を貰う。

C++では、「オブジェクト指向っぽいナニカ」が出きるようになりましたが、
多重継承という呪いのアイテムも付いてきました。

仮想関数やら、オーバーライドやらは、Cの延長線上では、
関数ポインタで実装するしかないわけですが、
ネイティブコンパイラであるが故に、「関数の順番」が非常に重要です。
#コンパイルしちゃった後は、オフセットでしか参照してないらしいので。

マイクロソフトも、MFCやらOLEやらで、七転八倒した形跡があります。
#そして、やっぱり辛くて、C#を産むわけですよ。

とまあ、反省することしきりですが、

筆者がC/C++を使わなくて済んでいるのは、
(例えば)python/PHPの作者がC/C++で実装しているお陰。

ありがたいことです。