普段 Web のぬるい言語しか触っていない C++ 初心者がハマったこと。
(なぜ C++ をやっているかというと VST プラグインを作成するのに C++ がほぼ必須だから。この話はまた改めて書く予定。)
配列がポインタ
関数に配列を渡そうと思ったところ、受け側の引数をどう書けばいいのか分からなかった。
が、どうやらポインタで書くらしい。
ということは、関数側では渡されたポインタの指す先が配列かどうかを知る術はない。
また、長さも分からない。そういう用途では vec
を使うっぽい。
C がそうなのは知っていたが、 C++ もそういう所は C の親戚らしい。 「配列かどうかが区別できないとかそんな訳ないだろ」と思って検索しまくって時間が溶けた。
初期値が 0 ではない
宣言だけして初期化していない変数は 0
ではなく適当な値が入っている。
という話は聞いたことはあったものの、 0
で初期化される言語に親しんだ時間が長すぎて無意識下に刷り込まれていたようだ。
enum の初期値がセットされている前提でロジックを組んでいたので意味不明な挙動になった。
(ここからはオーディオ系のプログラミングに特有なのだが、ロジックが間違っていてもノイズが入る程度で致命的な挙動の変化にはならない。「気のせいかな」と思って放置していて間違いに気づくのに3日かかった。)
インデックス例外が発生しない
配列の範囲外にアクセスした時、特に何も言われず何かしらを壊しながら処理が進んでしまう。 そして全然関係ないところで起こるアクセス違反。 どこかでポインタの指す先を誤って解放してしまったか?と思い込んで2時間ほど消えた。
「インデックスが範囲を超えたら例外が発生してプログラムが止まる」というのが、これまた無意識下に刷り込まれているため、「なんか間違ってたら実行時に気づくでしょ」という安心感(?)からロジックをあまり確認せずに軽率に実行する癖がついてしまっているようだ。 C++ でそれをやると間違えていても普通に気づかないので、何かあった時の時間の溶け方が半端ない。
思ったよりポインタでは事故らない
フレームワーク(JUCE)のサンプルに倣っているので、今のところポインタ関連の事故はほぼ起きていない。 まあオーディオ系のプログラミングなので、短いライフサイクルでオブジェクトを生成・破棄していないというのはありそう。ライフサイクルの長いオブジェクトが所有しているオブジェクトへの参照を渡していってるだけ。
事故り始めたらスマートポインタに乗り換えようかな。
時間を溶かさないための対策
とにかくアサーションを入れる。 他の言語に比べて実行時例外が全然出ないので、不安なところにアサーションをどんどん入れていく。間違っていたら IDE が動作を一時停止してくれる。 パフォーマンスが不安な場所は条件コンパイルでデバッグ用の処理を差し込んでいく。