C++のコーディングガイドライン

C/C++の書き方についてはGoogleが作ったC++のスタイルガイドが参考になります。 よい書き方/よくない書き方の例に加え、メリット/デメリットなども書いてあるため、自分のアプリケーションで採用するかどうかの判断材料になります。 また、cpplintというリンターもあります。

注釈

cpplintはもともとGoogleが開発していたオープンソースプロジェクトでしたが、現在ではGoogleの手を離れたプロジェクトになっているようです。

インクルードガード

1#ifndef Geometry_h
2#define Geometry_h 1
3
4// ヘッダーファイルの内容
5
6#endif

インクルードガード(もしくは #define ガード)は、 同じヘッダーファイルが複数回インクルードされるのを防ぐために ファイルの先頭に#ifndefを使ってチェックする書き方です。

Geant4のヘッダーファイルはすべてガードが定義されています。 自分が作るコードも同じようにガードするとよいです。

注釈

#ifndefを使う伝統的な書き方の代わりに、#pragma onceという書き方も広く使われています。 1行で書けて分かりやすい反面、C++の標準規格には含まれていない拡張機能です。 Googleスタイルガイドでは伝統的な書き方が推奨されていますが、 このドキュメント内のサンプルコードでは#pragma onceを使っている箇所もあります。

前方宣言

 1// #include "G4Event.hh"
 2// インクルードする代わりに前方宣言する
 3class G4Event;
 4
 5class EventAction : public G4UserEventAction
 6{
 7  public:
 8    void BeginOfEventAction(G4Event* aEvent);
 9    void EndOfEventAction(G4Event* aEvent);
10    // 引数はG4Eventへのポインタ型なので
11    // クラスの完全な定義がなくても
12    // 前方宣言だけでコンパイルできる。
13};

ヘッダーファイルをインクルードする代わりにclass クラス名;と書くことを前方宣言と呼びます。 「こういう名前のクラスがあるよ」ということをあらかじめお知らせすることで、コンパイルエラーを避けることができます。

注釈

前方宣言だけでコンパイルできるのは、 そのクラスをポインタ(G4Event*)や参照(G4Event&)として使う場合に限られます。 値そのものをメンバー変数として持ったり、 そのクラスのメソッドを呼び出したりする場合は、 クラスの完全な定義(=#include)が必要です。

また、ヘッダーで前方宣言していても、 実装ファイル(.cc)側でそのクラスのメソッドを呼び出す場合は、 結局そちらで#includeする必要があります。 前方宣言はあくまで「ヘッダーファイル側のコンパイルを軽くする」ための工夫です。

Geant4では多用されていますが、Googleスタイリングガイドでは、できるかぎり使わないことが推奨されています。 なので、僕はあまり使わないようにしています。

注釈

前方宣言を避けたほうがよいとされる理由は、 依存関係が見えにくくなったり(#includeならヘッダーを見ただけで依存先が分かる)、 値渡しやメンバー変数に変更したときにコンパイルエラーの原因が分かりにくくなったりするためです。

一方で、循環参照を避けたい場合や、 Geant4本体のような大規模フレームワークでコンパイル時間を短縮したい場合には、 前方宣言が積極的に使われています。

「理由がなければ#include、理由があれば前方宣言」くらいの判断軸で考えるとよさそうです。