XP (エクストリーム プログラミング)


C++ コーディング標準

C++ コーディング標準


このドキュメントはオリジナルの Java コーディング標準を C++ 用に変更したものです.

オリジナル:http://www.objectclub.jp/community/codingstandard/CodingStd.pdf

フィードバックを歓迎します.
フィードバック先: fujiwo@shos.info

  1. 方針

    このコーディング標準は,ソフトウェア開発プロジェクトにおいて C++ でコーディングする際のルール,推奨,および迷った時の指針を提供するものである.
    標準策定の方針は,読みやすくメンテナンスしやすいコードを書くことである.実際のコーディングにあたっては,プロジェクトメンバー全員がこのルールに合意していることが必要である.
    実プロジェクトにおいては,このコーディング標準をカスタマイズして用いることを推奨する.

  2. ファイル構成

    (1) ファイル名
    原則としてクラスは、そのクラス名 (大文字小文字の区別を含めて) の .h ファイル (必須) と .cpp ファイル (必須ではない) にする.
    但し、ローカルにしか使用されない (局所的な) クラスは,そのクラスが使われるグローバルなクラスのファイルに含めて良い.

    (2) ファイルの位置
    プロジェクトのルート ディレクトリを決める.サブ プロジェクトはその下にサブ ディレクトリを作成する.

  3. 命名規則

    (1) 原則として省略は行わない (但し以下の例外を除く)

    (2) スコープ(通用範囲)が狭いループ時のカウンタ,イテレータは,i, j, k という名前をこの順に使う).

    (3) スコープが狭い名前
    スコープが狭い変数名は,型名を略したものを使って良い.
    例: ServletContext sc = getServletContext();

    (4) クラス名
    先頭大文字.あとは区切りを大文字.
    CapitalizedWithInternalWordsAlsoCapitalized

    (5) 例外クラス名
    最後をExceptionとしたクラス名.
    ClassNameEndsWithException

    (6) 抽象クラス名
    抽象クラス名に適当な名前が無いとき,Abstract から始まりサブクラス名を連想させる名前を付ける.
    AbstractBeforeSubClassName

    (7) 定数
    大文字を “_” でつないだもの.
    UPPER_CASE_WITH_UNDERSCORES

    (8) メソッド名
    最初小文字で,あとは区切りを大文字.
    firstWordLowerCaseButInternalWordsCapitalized()

    (9) ファクトリメソッド(オブジェクトをnewするもの)
    X* newX()
    X* createX()

    (10) コンバータメソッド(オブジェクトを別のオブジェクトに変換するもの)
    X toX() const

    (11) 属性の取得メソッド
    X x() const
    X getX() const
    bool isEnabled() const

    (12) 属性の設定メソッド
    void setX(X value)

    (13) bool変数を返すメソッド
    is + 形容詞,can + 動詞,has + 過去分詞,三単元動詞,三単元動詞 + 名詞.
    bool isEmpty()
    bool empty() // だめ!’空にする’という動詞的な意味に取れるため良くない.
    bool canGet()
    bool hasChanged()
    bool contains(Object)
    bool containsKey(Key)
    理由: if, while文等の条件が読みやすくなる.またtrueがどちらの意味か分かりやすい.

    (14) bool変数
    形容詞,is + 形容詞,can + 動詞,has + 過去分詞,三単元動詞,三単元動詞 + 名詞.
    bool isEmpty_
    bool dirty_
    bool containsMoreElements_

    (15) 名前の対称性
    クラス名,メソッド名を付ける際は,以下の英語の対称性に気を付ける.

    add/remove
    insert/delete
    get/set
    start/stop
    begin/end
    send/receive
    first/last
    get/release
    put/get
    up/down
    show/hide
    source/target
    open/close
    source/destination
    increment/decrement
    lock/unlock
    old/new
    next/previous

    (16) 意味がとれる名前
    変数名から役割が読み取れる名前を好め.
    悪い例: copy(s1, s2)
    良い例: copy(from, to) あるいはcopy(source, destination)

    (17) 無意味な名前
    Info, Data, Temp, Str, Bufという名前は再考を要する.
    悪い例: double temp = Math::sqrt(b*b - 4*a*c);
    良い例: double determinant = Math::sqrt(b*b - 4*a*c);

    (18) 大文字小文字
    大文字と小文字は別な文字として扱われるが,それのみで区別される名前を付けてはならない.

    (19) その他
    その他,プロジェクトによっては以下の命名則を用いる場合がある.
    ローカル変数:
    lower_case_with_underscore
    private/protected変数:
    suffixUnderscore_

    ※ 注意: 二つの連続するアンダースコア __ を含む名前や,アンダースコアで始まりそれに大文字が続く名前,グローバル名前空間内におけるアンダースコアで始まる名前の全ては,処理系ために予約されているため使用できません.

  4. ガイドライン

    (20) コーディングスタイル

    ・ファイル ヘッダーを付ける

    //////////////////////////////////////////////////////////////////////////////
    // 内容          ファイルの説明
    // 作成・修正    2001/12/01 氏名
    // 修正          2002/07/01 氏名 修正内容
    // 著作権 会社名 2001-2002
    //////////////////////////////////////////////////////////////////////////////

    ・インデントは的確に
    ・インデントは 4 文字分
    ・改行やスペースの入れ方に統一感があるように.

    #include <iostream>

    // クラスの説明
    class Stack
    {
    public:
        // 要素の追加
        // @param item 追加する要素
        void push(Object item) {
            if (itemCapacity <= itemCount) {
                // ...
            } else {
                // ...
            }
        }

        // 先頭要素の取得.先頭要素は取り除かれる.
        // @return 先頭要素
        Object pop() {
        // ...
            return top;
        }
    };


    (21) 長い行
    一行は最大80桁とし,それを超える場合は行を分割する.分割の指針は,(1) ローカル変数を利用,(2)カンマで改行,(3)優先度の低い演算子の前で改行,とする.
    例:

    double length = Math::sqrt(Math::pow(Math::random(), 2.0) + Math::pow(Math::random(), 2.0));

    // 方針(1)
    double xSquared = Math::pow(Math::random(), 2.0);
    double ySquared = Math::pow(Math::random(), 2.0);
    double length = Math::sqrt(xSquared + ySquared);

    // 方針(2)
    double length = Math::sqrt(Math::pow(Math::random(), 2.0,
                                       Math::pow(Math::random(), 2.0);

    // 方針(3) return (this == obj
                                   || (this->parameter == obj->parameter
                                       && this->field == obj->field));

    (22) 長い宣言行
    クラス,メソッドの宣言が長い場合,(1):/throw節で改行,(2) カンマで改行とする.
    例:

    class LongNameClassImplemenation :
                    public AbstractImplementation,
                    public Serializable, public Cloneable
    {
        void longNameInternalIOMethod(int a, int b)
                    throw IOException {
            // …
        }
        void longMethodSignature(int a, int b, int c,
                                                   int d, int e, int f) {
            // …
        }
        // …
    }

    (23) クラス外の変数・関数

    main 関数を除き,原則としてクラス外部の変数や関数は使わない.

    (24) main 関数

    main 関数では,長いコードを書かない.せいぜいオブジェクトを一つ生成しそのメソッドを数回呼び出すだけにしておく.

    (25) public variable
    インスタンス変数は,極力publicにせず,妥当なアクセスメソッドを設けること.
    理由: オブジェクト指向の標準.クラスの内部状態に勝手にアクセスさせるのはよくない.ただし,以下の条件をすべて満たす場合,インスタンス変数をpublicにし,直接アクセスさせてもよい.
    l そのインスタンス変数が他のインスタンス変数と独立であり,単独で変更されても内部の整合性をくずさない.
    l どちらにしても,getX()/setX() メソッドを書く.
    l インスタンス変数の実装が将来に渡って変更されないことが根拠付けられる.
    また,上記に当てはまらない場合でも,極度に速度を気にする場合は,この限りではない.(ただし,慎重にコメントすること)
    例: Stackクラスにおいて,itemCount属性をpublicにしてはならないが,Pointクラスにおいてx, yをpublicにしてもいいかもしれない(極端に速度を気にする場合, ex. Java3D の Vector/Point クラス).

    Stack stack;
    stack.itemCount = 79; // ここで内部状態が崩れてしまう.

    Point point;
    point.x = 30; // 内部状態の整合性は崩れない.

    (26) 初期化
    初期化をあてにしない(参照がnullに初期化されているとか).また,2度初期化しない.

    悪い例:

    class PoorInitialization
    {
    private:
        static std::string name;

    public:
        PoorInitialization()
        {
            name = “initial_name”;
        }
    };

    String PoorInitialization::name = "initial_name";

    理由: 初期化に関するバグを最小化する.

    (27) static メンバー変数を避ける
    static メンバー変数 (クラス変数) は極力避ける.(static const 変数は除く)
    理由: static メンバー変数は,セミグローバルと言って良い.より文脈依存なコードを招き,副作用を覆いかくしてしまう.

    (28) const を好め
    クラス内の関数,変数,引数,一時的な変数を問わず const に出来るものは const にする.

    (29) private vs. protected
    privateよりは,protectedを使用すること.
    理由: privateは確実にそのクラス外からの使用をシャットアウトできるが,クライアントが,より細かいチューニングをsubclass化によって行うことを出来なくしてしまう.
    別法: private をより好んで使え.protected にしてしまうと以降,変更が起ったときにそれを継承している全クラスに影響が出てしまう.

    (30) get/set メソッド
    無闇にインスタンス変数へのアクセスメソッドgetX()/setX() を作成してpublicにすることは避ける.その必要性を検討し,もっと意味のあるメソッドにする.
    理由: インスタンス変数は,他のインスタンス変数に依存していることが多い.クラス内部の整合性を崩してはならない.

    (31) virtual 関数
    オーバーライドされる関数やデストラクタは virtual にする.

    (32) コピー
    コピーを許すクラスでは,必要に応じてコピー コンストラクタと代入演算子を作成する.
    許さないクラスでは,明示的にそれを禁止するため private なコピー コンストラクタと代入演算子を準備る.

    (33) 変数隠し
    スーパークラスの変数名と,同じ変数名を使う事は避けよ.
    理由: 一般的にはこれはバグである.もし意図があるならコメントせよ.

    (34) publicメソッド
    クラスのpublicメソッドは,「自動販売機のインターフェイス」を目標に.分かりやすく,使いかたを間違っても内部の整合性はこわれないように設計する.また,可能ならば契約による設計(Design by Contract)を行い,クラスの不変条件と共にメソッドの事前・事後条件をコードで表現せよ.

    (35) 状態取得と状態変更の分離
    メソッドは,「1つの事」を行うように設計せよ.特に,状態変更と状態取得の2つのサービスを1つのメソッドで行わない.状態を変更するメソッドのreturn値はvoidにせよ. Stackの例では, top() とremoveTop() の2つの方が,pop() より良い.
    理由1: 1つの事を行うメソッドの方が分かりやすい(Stackの例は,慣用の方が強い為,pop() が好まれるだけ).
    理由2: 並行性の制御,例外の安全保証がしやすい(参考: C++では,pop()メソッドが例外安全に出来ない理由で,標準ライブラリではpop()は値を返さない仕様となっている).
    理由3: サブクラス化による拡張がしやすい.

    (36) メソッドの多重定義
    引数のタイプによるメソッドのオーバーロードはなるべく避ける(引数の数が違うものはOKである).特に,継承と絡むと厄介である.
    例:
    × : draw(Line), draw(Rectangle)
    ○ : drawLine(Line), drawRectangle(Rectangle)
    ○ : draw(Shape)

    (37) デフォルトコンストラクタ
    可能ならいつでもデフォルトのコンストラクタ(引数がないもの)を用意せよ.

    (38) 抽象クラス
    抽象クラスでは,no-op のメソッドを書くより,明示的に 純粋仮想関数とせよ.また,共有可能なデフォルトの実装を用意できるなら,それを protected とし,サブクラスが1行で処理を書けるようにせよ.
     
    (39) 宣言と初期化
    ローカル変数は,初期値と共に宣言せよ.
    理由: 変数の値に関する仮定を最小化する.
    悪い例:

    void f(int start) {
        int i, j; // 初期値なしの宣言
        // 多くのコード
        // ...
        i = start + 1;
        j = i + 1;
        // i, jを使う
    }

    良い例:

    void f(int start) {
        // 多くのコード
        // ...
        // 使う前,はじめて宣言と初期化
        int i = start + 1;
        int j = i + 1;
        // i, jを使う
    }

    (40) ローカル変数の再利用は悪
    ローカル変数を使い回しするより,新しいものを宣言して初期化せよ.
    理由: 変数の値に関する仮定を最小化する.
    理由: コンパイラの最適化を助ける.
    悪い例:

    void f(int N, int delta) {
        int i; // 初期値なしの宣言
        for (i = 0; i < N; i++) {
            // iを使う
        }
        for (i = 0; i < N; i++) {// またiを使う
            if (...) {
                break;
            }
        }
        if (i != N) { // 最後まで回ったかの判定にiを使っている
            // ...
        }
        i = N - delta*2; // またまた再利用
        // ...
    }

    良い例:

    void f(int N, int delta) {
        for (int i = 0; i < N; i++) {
            // iを使う
        }
        for (int i = 0; i < N; i++) {
            // 別のiを使う
            if (...) {
                found = true;
                break;
            }
        }
        if (found) {
            // ...
        }
        int total = N - delta*2; // 別の意味ある変数
        // ...
    }

    * 但しデフォルトの VC++ コンパイラはこれに対応していない.

    (41) if/while条件中の “=”
    if, whileの条件には,代入 “=” を使ってはならない.
    理由: ほとんどの場合,バグである.或るコンパイラは,このような記述に警告を出す.

    (42) キャスト
    キャストはできるだけ避ける.止むを得ない場合は C++ の新しいキャストを使用する.

    (43) 例外クラス
    例外クラスは大域的な性格をもち,多用するとプログラムの流れを読みにくくしてしまうことを認識する.

    (44) エラー
    エラー時の処理に assert を使うべきか例外処理を使うべきか,他の方法 (戻り値など) を用いるべきかを考慮し,区別してこれらを用いる.
    注意: ユーザ入力チェックなどをassertしてはいけない.バグを捕まえるためにassertせよ.

    (45) メソッド引数の変更は悪
    原則としてメソッドの引数は入力であり,出力としては使わないこと.すなわちメソッド内部で引数の状態を変更するメソッドを呼ばないこと.出力引数に新たなオブジェクトを代入しないこと(可能ならconstとせよ).

    悪い例:

    void moveX(Point p, int dx) {
        p.setX(p.getX() + dx); // 引数を変更している(なるべく避ける)
    }

    void moveX(Point p, int dx) {
        p = Point(p.getX() + dx, p.getY());
        // これは呼び出し側に伝わらない
    }

    例外: パフォーマンスを気にする場合

    (46) メソッド引数の名前
    メソッドの引数は,読みやすいものにすること.特に,インスタンス変数と重なった場合,thisを活用し,引数の読みやすさを犠牲にしないこと.

    悪い例:

    void reset(int x_, int y_) {
        x = x_;
        y = y_;
    }

    良い例:

    void reset(int x, int y) { // 引数名を x_, y_ などとしない
        this->x = x;
        this->y = y;
    }

    (47) toString()
    toString() メソッドは可能ならいつでも実装すること.
    理由: いつでもプリントできる.デバッグが容易になる.

    (48) switch,if/elseの繰り返しは悪
    switch文で分岐する処理が現れた時には,よくない設計の兆候だと考え,ポリモーフィズムで実現できないか再考する.特に同じようなswitchが2箇所以上現れたら,必ずポリモーフィズム,FactoryMethod,Prototype パターン等でリファクタリングすること.if/elseの連続も同様.

    (49) コレクション
    環境が許せば,C++ 標準のコレクション クラスを用いよ.

    理由1: より簡潔で論理的,一貫性のあるメソッド名が使える.
    理由2: 標準コレクション クラスのインターフェイスにより,インターフェイスを変更せずに実装を取り替えることが可能.
    理由3: より高速なコードが書ける(可能性がある).

  5. コメント

    (50) コメント
    public なクラス,関数,変数にはコメントを付ける.

    (51) 長いコメント
    コメントが複数行に渡る場合は,最初の短い一文で何が言いたいかを書き,その後に長いコメントを付けること.

    (52) // vs. /* */
    メソッドやクラスの内部的なコメントは,/* */ か,// を使用する.長さで判断してよい.1行コメントは,// を使った方がよい.
    例1:

    /*
    * 戦略:
    * 1. まずnodeを探す
    * 2. cloneする
    * 3. inserterにcloneを追加要請
    * 4. 成功したら,nodeを削除
    */

    例2:

    int index = -1; // -1はinvalidな値を意味する

    参考: この方がさらによい.
    static const int INVALID= -1;
    int index = INVALID;


  6. パフォーマンス

    (53) まず計測
    パフォーマンス改善はまず計測から始めよ.当てずっぽうではだめ.

    (54) new
    newは時間が掛かる.ヘビーなループの中で new が繰り返し呼ばれることを出来るだけ避ける.

    (55) コピー
    巨大なオブジェクトのコピーは時間が掛かる.関数の引数がオブジェクトの場合は,値渡しとせず const な参照渡しとする.

  7. その他

    (56) 自分で新しく作る前に相談
    他人が作成したクラスに対するある操作が新たに必要となるとき,自分でそのクラスをextendsして新たなクラスを作成したり,そのクラスをインスタンス変数として持つクラスを作成するより,まずそのクラスの作成者に相談すること.汎用的な形でその要望を満たしてくれれば,全体をコンパクトにできる.

    (57) 複雑な設計は悪
    設計で迷った場合,多くのケースはシンプルさを重視する.
    後のメンテナビリティにもシンプルさは重要である.

    (58) パフォーマンス調整は測定後
    最初からパフォーマンスを気にしたコーディングをするべきではない.読みやすさ,保守のしやすさを優先する.パフォーマンスは測定してから改善する.

    (59) トリッキーなコードは悪
    平均プログラマに分かるようなコードを書く.演算子の順序,初期化に関する規則など,誰もが必ずしも自信をもって答えられないような仮定を持ち込まず,() を使って演算順序を明確にしたり,明示的な初期化を行った方が読みやすい.
    悪い例:

    return cond == 0 ? a < b && b < c : d == 1;

    良い例:

    return (cond == 0) ? ((a < b) && (b < c)) : (d == 1);

    悪い例:

    // 単位行列を作るが,時間もかかるし誰も読めない.
    for (int i = 1; i <= N; i++)
        for (int j = 1; j <= N; j++)
            M[i-1][j-1] = (i/j)* (j/i);

    (60) 100%正しいことはない
    ここに書かれていることに,100% 準拠する必要はない.迷ったら考えを整理し,相談すること.十分な理由があってルールから外れることはよくある.コミュニケーションができるチームの助けとなることが,このコーディング標準の目的である.

  8. 参考資料

    Kenji Hiranabe, Javaコーディング標準 (オリジナル)
    http://ObjectClub.esm.co.jp/eXtremeProgramming/CodingStd.doc

    Copyright © 2000,2001 Eiwa System Management, Inc. Object Club Kenji Hiranabe 02/07/12