こんばんは、mulfunctionです。
「研究者こそコーディング規約を守るべき」の回でも書いた通り、数値実験用のプログラムにバグが混入する恐れを極力減らし、プログラム内で何が起こっているのかを把握しやすくするためにも、研究活動の中でプログラミングをする研究者にとってコーディング規約を遵守することは必須だと考えています。
とはいえ、システム屋さんほど念入りにコードレビューをしている訳ではなく、どのような規約で書くかも、自分の調べた範囲で規約を遵守する、半我流になっています。コードの質を少しでも高めるため、時々コーディング規約を調べて取り入れたりしているのですが、今日試してみて効果があったものをご報告。
「Google C++ スタイルガイド」に書かれていたのですが、Googleの規約では前方宣言をなるべく避け、必要なヘッダファイルは全てその .h ファイル内でインクルードするように推奨しているのですね。むしろ私は、コンパイル時間短縮のためにも、前方宣言を多用し、必要なヘッダファイルは対応する .cpp ファイル内でインクルードするようにしていました。
つまり私は、
// --- in A.h --- #pragma once class B; // クラス B を前方宣言 class A { private: std::shared<B> m_b; // クラス A 内のメンバとしてクラス B のインスタンスを保持 }; // class A // --- in A.cpp --- #include "B.h" #include "A.h" // ...定義... //
のような書き方をしていたのですが、Googleのコーディング規約では、
// --- in A.h --- #pragma once #include "B.h" // 必要はヘッダを全てインクルード class A { private: std::shared<B> m_b; // クラス A 内のメンバとしてクラス B のインスタンスを保持 }; // class A // --- in A.cpp --- #include "A.h" // ...定義... //
のように書きましょうということですね。
早速試してみたところ、クラス設計自体が大いに改善されました! 今まではクラス A と クラス B がそれぞれのメンバ変数としてお互いのインスタンスを持ち合うようなコードになっていたのが、前方宣言を止めて全ヘッダをインクルードするようにすると、コンパイルエラーが出て相互参照状態になっているのが分かりやすくなりました。この相互参照状態が解消されるように、メンバの型やメソッドに渡す引数を整理していったところ、互いにインクルード必要なヘッダファイルの数が減り、クラス間の繋がりが非常に疎なものになりました!
本職のシステム屋さんならば、コードを書き始める前の設計段階で相互参照のようなデザインにはならないのかもしれませんね。その点、私はプログラミングスキルもまだまだですし、研究用のプログラムは研究の進捗に合わせて徐々に仕様が追加され*1、モデルの仕様変更に合わせた改変もしょっちゅうなので、なかなかコーディング前に全体設計を決めるのが難しいと感じています*2。
Google C++ スタイルガイドには、そのクラスが何を必要としているのかを明示するために、必要なヘッダファイルは全てインクルードすることを推奨していますが、確かにクラス間の依存関係が明白になって、非常に効果を上げました。まだチェックしていない他の規約についても、同様のご利益は大いに期待できそうですし、早急に取り入れていきたいと思います。
Google C++ スタイルガイド(日本語全訳) Google C++ Style Guide (Japanese)
しかし、コードリファインで構造がスッキリしたときの快感は素晴らしいですね。難しい数学の問題を解けたときのような、「ああ仕組みが分かった!」という感覚。やっぱりプログラミングは楽しいです。
それでは、また。
/mulfunction