フリーソフト

« 2015年5月 | トップページ | 2016年1月 »

その他のC++の機能

このドキュメントは、Google C++ Style Guideを翻訳、要約したものです。省略した部分や意訳した部分が多くあります。原文の意図が伝わるよう注意しましたが、誤りが含まれているかもしれません。正確な情報については、必ず原文を参照してください。

前へ | 目次 | 次へ

その他のC++の機能

参照引数

参照渡しのパラメータにはconstを付けること。

Googleのコードでは、入力引数は値渡し、あるいはconstの参照渡しとし、出力引数はポインタ渡しとしている。入力引数はconstのポインタでもよい。

入力引数においてはconst T& よりもconst T* がよい場合がある。例えば、

  • NULLポインタを渡したい場合
  • 関数内でポインタや参照を入力引数に保存する場合

具体的な理由がない限りはconst T* ではなくconst T& を選択すること。

右辺値参照(C++11)

右辺値参照はムーブコンストラクタ、ムーブ代入オペレータを定義するためだけに使うこと。std::forward関数は使用禁止。

関数のオーバーロード

オーバーロード関数(コンストラクタを含む)は、呼び出したときに何が起こるかが理解できる場合にのみ利用すること。

オーバーロードを使う前に、関数名に引数に関する情報を付加することを検討すること。例えば、Append()ではなく、AppendString() やAppendInt() とするなど。

デフォルト引数

関数のデフォルト引数は使用禁止。代わりに関数のオーバーロードを使用すること。

例外は以下の通り。

  • 関数が .ccファイル(もしくは名前なし名前空間)内でスタティックな場合。
  • コンストラクタ。
  • 可変長引数リストをシミュレートするためにデフォルト引数を使う場合。
// 空のAlphaNum をデフォルト引数に使用し、最大で4つの引数をサポート
string StrCat(const AlphaNum &a,
              const AlphaNum &b = gEmptyAlphaNum,
              const AlphaNum &c = gEmptyAlphaNum,
              const AlphaNum &d = gEmptyAlphaNum);

可変長配列とalloca()

可変長配列やalloca() は使用禁止。代わりにstd::vectorやstd::unique_ptr<T[ ]> のような安全なアロケータを使用すること。発見困難なメモリ上書きのバグを引き起こす恐れがある。

フレンド

常識の範囲内でfriendクラスやfriend関数を使用してもよい。

フレンドは同じファイル内に定義すること。privateメンバの内容を確認するために他のファイルを探す手間が軽減できる。

フレンドはカプセル化の境界を拡大するが破壊することはない。そのため、メンバをpublicにするよりもよい場合がある(例:あるクラスにのみアクセス権を与えたい場合など)。

例外

C++の例外を使用してはならない。

GoogleのC++コードは例外を扱うようには書かれておらず、例外を生成するコードを統合することは困難である。例外の代わりにエラーコードやアサーションなどを使用すること。

このルールは、経験的な根拠に基づくものである。

C++に加えられた例外も使用禁止(oexcept、std::exception_ptr、std::nested_exception)。

Windowsでは例外がある。「Windowsのコード」を参照のこと。

実行時型情報(RTTI)

実行時型情報(RTTI)の使用を避けること。

RTTIを使用すると実行時にオブジェクトのC++クラスを問い合わせることができる(typeidやdynamic_castを使用)。しかしながら、実行時にオブジェクトの型が必要になるのは、たいていクラス階層の設計に欠陥があるということを暗示している。RTTIの乱用はコードの保守を困難にする。

クラスごとに処理を分岐したい場合は、RTTIではなく以下に示す代替方法の利用を検討すること。

  • 仮想メソッドを使ってサブクラスごとに異なる処理を実行する。オブジェクト自身に処理内容を置く方法。
  • Visitorデザインパターンのようなダブルディスパッチを使う。オブジェクトの外側に処理を置く方法。

プログラムロジック的に、与えられた基本クラスのインスタンスが特定の派生クラスのインスタンスであることが保証されている場合はdynamic_castが利用できる。そのような状況では、たいていstatic_castを使うこともできる。

キャスト

static_cast<>() のようなC++のキャストを使用すること。int y = (int)x; や int y = int(x); のようなキャスト形式は使用禁止。

  • 値を変換するCスタイルのキャストの代わりにstatic_castを使用すること。また、クラスポインタを親クラスへ明示的にキャストする場合もstatic_castを使用すること。
  • const装飾子を取り除く場合はconst_castを使用すること。
  • 整数とその他のポインタタイプの間の安全でない変換にはreinterpret_castを使用すること。変換の内容やエイリアス問題を理解している場合のみ使用すること。

dynamic_castについては「実行時型情報(RTTI)」を参照のこと。

ストリーム

ストリームはロギングのためだけに使用すること。それ以外ではprintfライクなルーチンを使用すること。

前置インクリメントと前置デクリメント

イテレータやその他テンプレートオブジェクトには、インクリメント演算子およびデクリメントオペレータを前置形式(++i)で使用すること。単純なスカラー値(オブジェクト以外)はどちらの形式でもよい。

戻り値が無視される場合、後置形式ではiのコピーが求められる。iがイテレータや非スカラー型の場合、コピーのコストが高くなるため、前置形式の方が効率的となる。

constの使用

constが意味をなすときは必ず使用すること。C++11ではconstexprを使用したほうがよい。コンパイル時の型チェックにより、早い段階でエラーが発見できるようになる。

  • 参照やポインタで渡された引数が、関数内で変更されない場合はconstとする。
  • 可能な限りメソッドはconstで宣言する。アクセッサは通常はconstである。その他のメソッドについては、データメンバを変更せず、非constメソッドを呼び出さず、データメンバへの非constポインタや非const参照を返さない場合はconstとする。
  • クラス生成後、変更の必要がないデータメンバはconstにする。

mutableキーワードを使用してもよいが、スレッドでの利用は安全ではない。最初にスレッドの安全性を慎重に考慮すること。

constexprの使用(C++11)

C++11ではconstexprを使用して定数を定義し、初期化すること。

整数型

組み込みのC++整数型のうち、使用してもよいのはintのみ。異なるサイズの変数が必要な場合は、

にあるint16_tのような固定幅の整数型を使用すること。変数の値が2^31(2GiB)以上になりうる場合は、int64_tのような64ビット型を使用すること。計算過程で、より大きい型が要求される場合があるため、疑わしい場合はより大きい型を選択すること。

C++における整数型はコンパイラや環境によってサイズが変化する。

にはint16_t、uint32_t、int64_tなどの型が定義されている。整数のサイズに保証が必要なときは、shortやunsigned long longではなく、常にこれらの型を使用すること。size_tやptrdiff_tのような標準型を使用してもよい。

intが32ビットより大きいと仮定してはならない。64ビットの整数型が必要な場合は、int64_tやuint64_tを使用すること。数値が大きくなりそうな場合もint64_tを使用すること。

正当な理由がない限り、uint32_tのようなunsigned整数型を使用してはならない。数値が負にならないことを示す(自分自身へのドキュメント)ためだけにunsigned型を使用してはならない。代わりにアサーションを使用すること。

整数型の変換に注意すること。整数変換と整数拡張は非直感的なふるまいを引き起こすことがある。

64ビットの移植性

64ビットでも32ビットでも動作するコードを書くべきである。print、比較、構造体のアラインメント問題に注意すること。

  • printf() 指定子は32ビットと64ビットシステムで互換性がないものがある。C99では互換形式の指定子がいくつか定義されているが、MSVC 7.1では認識されないものがある。その場合は、標準インクルードファイルinttypes.hに独自の定義を記述しなくてはならないことがある。
    // printf macros for size_t, in the style of inttypes.h
    #ifdef _LP64
    #define __PRIS_PREFIX "z"
    #else
    #define __PRIS_PREFIX
    #endif
    
    // Use these macros after a % in a printf format string
    // to get correct 32/64 bit behavior, like this:
    // size_t size = records.size();
    // printf("%"PRIuS"\n", size);
    
    #define PRIdS __PRIS_PREFIX "d"
    #define PRIxS __PRIS_PREFIX "x"
    #define PRIuS __PRIS_PREFIX "u"
    #define PRIXS __PRIS_PREFIX "X"
    #define PRIoS __PRIS_PREFIX "o"
    
    使用禁止 こちらを使用 備考
    void * %lx %p
    int64_t %qd, %lld %"PRId64"
    uint64_t %qu, %llu, %llx %"PRIu64", %"PRIx64"
    size_t %u %"PRIuS", %"PRIxS" C99では%zu
    ptrdiff_t %d %"PRIdS" C99では%td
    PRI* マクロは独立の文字列に展開されることに注意。非定数のフォーマット文字列を使用する場合は、名前ではなくマクロの値をフォーマットに挿入する必要がある。指定子の長さなどを%の後に含めることもできる。例えば、printf("x = %30"PRIuS"\n", x) は32ビットLinuxではprintf("x = %30" "u" "\n", x) に展開され、コンパイラはprintf("x = %30u\n", x) として扱う。
  • sizeof(void *) != sizeof(int) であることに注意。ポインタサイズの整数が必要な場合はintptr_tを使用すること。
  • ディスクに保存されている構造体のアラインメントに注意すること。int64_tやuint64_tのメンバを持つクラス/構造体は、64ビットシステムではデフォルトで8バイトアラインとなる。このような構造体を32ビットと64ビットシステムの間でディスク上に共有する場合、同じようにパックしなくてはならない。ほとんどのコンパイラでは構造体のアラインメントを変更する方法を提供しており、gccでは __attribute__((packed)) を使用する。MSVCでは #pragma pack() と__declspec(align()) を使用する。
  • 64ビット定数を作成するときは、LLまたはULL接尾辞を使用すること。
    int64_t my_value = 0x123456789LL;
    uint64_t my_mask = 3ULL << 48;
  • 32ビットと64ビットシステムで異なるコードが必要な場合は、#ifdef _LP64を使用すること。このような処理は可能な限り避け、変更を局所的にとどめること。

プリプロセッサのマクロ

マクロは十分に注意して使用すること。マクロよりもインライン関数やenum、const変数を使った方がよい。

コードをインライン化するマクロの代わりには、インライン関数が使用できる。定数を格納するマクロの代わりには、const変数が使用できる。長い変数名を省略するためのマクロの代わりには、参照が使用できる。マクロを使用しない方法を事前によく検討すること。

マクロを使用する場合はできるだけ以下のルールに従うこと。マクロに起因する多くの問題を避けることができる。

  • .hファイルにマクロを定義してはならない。
  • 使用する直前にマクロを #defineし、直後に #undefすること。
  • 既存のマクロを #undefして独自のマクロに置換してはならない。代わりに、ユニークな名前を付けること。
  • C++の構造を不安定に拡大するようなマクロを使用してはならない。少なくともその振る舞いをきちんと文書化すること。
  • ## を使用して、関数名、クラス名、変数名を生成しない方がよい。

0とnullptr/NULL

整数には0を、実数には0.0を、ポインタにはnullptr(もしくはNULL)を、文字には’\0’を使用すること。

sizeof

sizeof(型) ではなくsizeof(変数名) を使用した方がよい。

変数の型が変更されても、sizeof(変数名) は適切に更新される。特定の変数に関連しないコードではsizeof(型) を使用してもよい。

auto(C++11)

局所変数には型名ではなくautoを使用すること。可読性が高められる場合は通常の型宣言を使用してもよい。

局所変数以外(ファイル変数、名前空間変数、メンバ変数など)にautoを使用してはならない。auto型の変数を中括弧の初期化リストで初期化してはならない。

中括弧の初期化リスト

中括弧の初期化リストを使用してもよい。

C++03では、配列や構造体(コンストラクタなし)を中括弧の初期化リストで初期化することができた。

struct Point { int x; int y; };
Point p = {1, 2};

C++11では、この構文が一般化され、すべてのオブジェクト型が中括弧の初期化リストで作成できるようになった。

// Vectorは要素のリストをとる
vector<string> v{"foo", "bar"};

// 細かい違いはあるが基本的に同じ
vector<string> v = {"foo", "bar"};

// ‘new’ に使えるやり方
auto p = new vector<string>{"foo", "bar"};

// mapはペアのリストをとる
map<int, string> m = {{1, "one"}, {2, "2"}};

// 返り型に暗黙的に変換される
vector<int> test_function() { return {1, 2, 3}; }

// リストの要素を順に処理
for (int i : {-1, -2, -3}) {}

// 関数の呼び出し
void TestFunction2(vector<int> v) {}
TestFunction2({1, 2, 3});

autoのローカル変数に中括弧の初期化リストを代入してはならない。要素が1つの場合に混乱が起きる。

auto d = {1.23};  // d は std::initializer_list<double>型

auto d = double{1.23};  // Good -- d は double型

書式については「中括弧初期化リストの書式」を参照のこと。

ラムダ式

適切な場所でラムダ式を使用すること。デフォルトのラムダキャプチャは使用禁止。すべてのキャプチャを明示的に記述すること。

  • デフォルトのキャプチャは使用禁止。例えば、[=](int x) { return x + n; } ではなく[n](int x) { return x + n; } と書くこと。これにより、読み手はnがキャプチャされることがすぐに理解できる。
  • 名前なしラムダを短くキープしておくこと。ラムダのボディが5行を超えるようなら、ラムダに名前をつけるか、ラムダの代わりに名前付き関数を使用したほうがよい。
  • ラムダの返り型を明示的に指定したほうが読み手にとって有用な場合はそうすること。

書式については「書式-ラムダ式」を参照のこと。

テンプレート・メタプログラミング

複雑なテンプレート・プログラミングを避けること。

チームの平均的なメンバがそのコードを十分に理解しメンテナンスできるか、エラーメッセージの意味が理解できるか、呼び出す関数のフローをトレースできるか、を考えること。再帰的なテンプレートのインスタンス化や、型リスト、式テンプレート、関数オーバーロード検出のためのSFINAEやsizeofトリックなどは行き過ぎの恐れがある。

テンプレート・メタプログラミングを使用する場合は、複雑さを最小化するために相当な努力が必要になると考えなくてはならない。できる限りメタプログラミングを隠ぺいし、ヘッダを読みやすくすること。トリッキーなコードには確実にコメントを書くこと。ユーザがミスしたとき、コンパイラが発するエラーメッセージにも注意を払わなくてはならない。エラーメッセージが理解可能なものとなるよう、必要に応じてコードを調整すること。

Boost

Boostは許可されたライブラリのみ使用すること。

現在、許可されているものを示す。

機能 ヘッダ
Call Traits boost/call_traits.hpp
Compressed Pair boost/compressed_pair.hpp
Boost Graph Library (BGL) boost/graph
*ただし、以下を除く
adj_list_serialize.hpp(シリアル化)
boost/graph/parallel/*(並列アルゴリズム)
boost/graph/distributed/*(分散アルゴリズム)
プロパティマップ boost/property_map
*ただし、以下を除く
boost/property_map/parallel/*(並列プロパティマップ)
イテレータの一部 boost/iterator/iterator_adaptor.hpp
boost/iterator/iterator_facade.hpp
boost/function_output_iterator.hpp
Polygonの一部 boost/polygon/voronoi_builder.hpp
boost/polygon/voronoi_diagram.hpp
boost/polygon/voronoi_geometry_type.hpp
ビットマップ boost/bimap
統計分布と統計関数 boost/math/distributions
Multi-index boost/multi_index
ヒープ boost/heap
コンテナ boost/container/flat_map
boost/container/flat_set

他のライブラリの追加も積極的に検討している。将来、リストが拡張されるかもしれない。

以下のライブラリも許可されているが、C++11の標準ライブラリに取って代わられたため推奨されない。

  • boost/array.hppのArray:代わりにstd::arrayを使用すること。
  • boost/ptr_containerのPointer Container:代わりにstd::unique_ptrのコンテナを使用すること。

C++11

C++11(C++0xとして知られていたもの)のライブラリと言語拡張は適切に使用すること。C++11の機能をプロジェクトで使用する前に、他の環境への移植性を考慮すること。

以下のC++11の機能は使用してはならない。

  • 返り値の型が後ろに続く関数(ラムダ関数以外)。例えば、int foo() の代わりにauto foo() -> int と書くもの。既存の関数宣言とスタイルの一貫性を維持するため。
  • コンパイル時有理数(<ratio>)。テンプレートヘビーなインターフェーススタイルに関係しているという懸念のため。
  • <cfenv>と<fenv.h>ヘッダ。多くのコンパイラがこれらの機能をサポートしていないため。

  • デフォルトのラムダキャプチャ。

前へ | 目次 | 次へ

Google特有のマジック

このドキュメントは、Google C++ Style Guideを翻訳、要約したものです。省略した部分や意訳した部分が多くあります。原文の意図が伝わるよう注意しましたが、誤りが含まれているかもしれません。正確な情報については、必ず原文を参照してください。

前へ | 目次 | 次へ

Google特有のマジック

所有権とスマートポインタ

動的に割り当てられたオブジェクトのオーナーは単一かつ固定されていることが望ましい。所有権を転送する場合はスマートポインタを使用することが望ましい。

動的な割り当てをする場合は、それを割り当てたコードに所有権をキープしておくことが望ましい。他のコードがそのオブジェクトにアクセスする必要がある場合は、コピーを渡すか、所有権を転送することなくポインタか参照を渡すこと。あるいは、std::unique_ptrを使って明示的に所有権を転送することが望ましい。

特別な理由がない限り、所有権を共有するコードを書いてはならない。負荷の大きいコピー操作を避けるため。パフォーマンス上の利益が大きく、かつ内在するオブジェクトが変化しない場合のみ所有権を共有してもよい(例:std::shared_ptr

)。所有権を共有する場合は、std::shared_ptrを利用することが望ましい。

scoped_ptrは使用禁止(古いC++との互換が必要な場合を除く)。std::auto_ptrも使用禁止。代わりにstd::unique_ptrを使用すること。

cpplint

cpplint.pyでスタイルのエラーが検出できる。

http://google-styleguide.googlecode.com/svn/trunk/cpplint/cpplint.py

前へ | 目次 | 次へ

クラス

このドキュメントは、Google C++ Style Guideを翻訳、要約したものです。省略した部分や意訳した部分が多くあります。原文の意図が伝わるよう注意しましたが、誤りが含まれているかもしれません。正確な情報については、必ず原文を参照してください。

前へ | 目次 | 次へ

クラス

コンストラクタでやるべきこと

コンストラクタでは、複雑な初期化(失敗する可能性のある初期化、仮想メソッドの呼び出しが必要な初期化など)をしてはならない。

複雑な初期化が必要な場合はファクトリ関数かInit() メソッドを使用すること。

初期化

すべてのメンバ変数に対して、クラス内初期化子(in-class initializer)を用意するか、コンストラクタ(デフォルトコンストラクタでも可)を書かなくてはならない。

クラス内初期化子とは、int count_ = 17やstring name_{"abc"} のようにメンバ変数を宣言することを指す。

明示的なコンストラクタ

暗黙的呼び出しを避けるため、引数がひとつのコンストラクタにはexplicitキーワードをつけること。

2つ目以降のパラメータにデフォルト値が設定されている場合もexplicitをつけること。 例:Foo::Foo(string name, int id = 42)。

コピーコントラクタとムーブコントラクタは例外。また、他のクラスとの透過ラッパーのように意図的に暗黙的な呼び出しをするようなクラスも例外。このような例外はコメントに明記しておかなければならない。

また、引数がstd::initializer_list のみのコンストラクタはexplicitでなくてもよい。

MyType m = {1, 2};
MyType MakeMyType() { return {1, 2}; }
TakeMyType({1, 2});

コピー/ムーブ可能な型

必要な場合のみコピー/ムーブを有効にすること。不要な場合は明示的にそれらを無効にすること(= deleteなど)。

コピーの振る舞い(計算的に複雑なものを含む)が一見して分からないような場合は、コピー可能にしてはならない。コピー可能にする場合は、コピーコンストラクタとコピー代入オペレータの両方を定義すること。コピー可能な型において、ムーブ操作がより効果的な場合はムーブ可能とする。その際はムーブコンストラクタとムーブ代入オペレータの両方を定義すること。ムーブの正当性が明確ならば、ムーブのみ可能な型としてもよい。

スライスのリスクがあるため、派生元のクラスでは代入オペレータやパブリックなコピー/ムーブコンストラクタを定義してはならない(同様に、そういったメンバを持つクラスから派生してはならない)。コピー可能な基本クラスが必要な場合は、仮想メソッドClone()と、protected属性のコピーコンストラクタを定義する。派生クラスでは、このコピーコンストラクタを使ってClone() を実装する。

Delegating/Inheritingコンストラクタ

コードの重複が減らせる場合は、Delegating/Inheritingコンストラクタ(C++11の機能)を使用する。

Delegatingコンストラクタの例。

X::X(const string& name) : name_(name) {
  ...
}
X::X() : X("") {}

Inheritingコンストラクタの例。

class Base {
 public:
  Base();
  Base(int n);
  Base(const string& s);
  ...
};

class Derived : public Base {
 public:
  using Base::Base;  // Baseのコンストラクタがここで再宣言される
};

構造体 vs. クラス

構造体はデータ受け渡しのための受動的なオブジェクトにのみ使用すること。それ以外にはクラスを使用すること。

構造体のデータフィールドには、メソッドを通じてではなく直接アクセスすること。メソッドはデータメンバを設定するためだけに使用すること。例えば、コンストラクタ、デストラクタ、Initialize()、Reset()、Validate() など。これ以上の機能が必要な場合はクラスを使用したほうがよい。迷う場合はクラスを使用すること。

STLとの一貫性を保つため、functorsとtraitsについては構造体を使用してもよい。

構造体とクラスでは、メンバ変数の命名規則が異なることに注意。

継承

継承を多用してはならない。継承よりもコンポジション(インスタンスをメンバにすること)を使った方がよい。継承する場合はpublic継承とすること。

private継承したい場合は、代わりに基本クラスのインスタンスをメンバにすべき。

必要に応じてデストラクタを仮想関数にすること。クラスが仮想関数を持つ場合は、デストラクタを仮想関数にすべき。

protected 宣言のメンバ関数を制限すること。データメンバはprivateにすること。

仮想関数や仮想デストラクタのオーバーライドを、overrideやfinalキーワード(C++11以前のコードはvirtualキーワード)を使って明示的に示すこと。これらの指定子はドキュメント化の役割を果たす。記述子がない場合、読み手は、関数やデストラクタが仮想かどうか判断するため、問題のクラスのすべてのアクセッサをチェックしなければならない。

多重継承

多重継承は、実装を持つ基本クラスが1つで、それ以外の基本クラスが純粋インタフェース(Interface接尾辞で終わるもの)の場合のみ許される。

Windowsでは例外がある。「Windowsのコード」を参照のこと。

インタフェースクラス

以下の条件を満たすクラスは、その名前にInterfaceという接尾辞を付けることができる。ただし必須ではない。

  • Publicな純粋仮想関数(”=0”)とスタティックメソッド、仮想デストラクタのみを持つ。
  • 非スタティックなデータメンバは持たなくてもよい。
  • コンストラクタは不要。定義する場合は、引数なしで、protectedで宣言すること。
  • サブクラスの場合は、基本クラスが上記の条件を満たし、Interface接尾辞を持つこと。

ンタフェースクラスは純粋仮想関数を持つため、直接インスタンス化することはできない。派生クラスのデストラクタが呼び出されるよう、仮想デストラクタを宣言すること。

演算子のオーバーロード

特別な事情がない限り、演算子をオーバーロードしてはならない。また、ユーザ定義のリテラルを作成してはならない。

必要な場合はEquals()のような通常関数を定義すればよい。

「コピー/ムーブ可能な型」「関数のオーバーロード」も参照のこと。

アクセス制御

データメンバはprivateとし、必要に応じてアクセス関数を用意すること(Google Testを使うときはtest fixtureクラスのデータメンバをprotectedにしてもよい)。メンバ変数名はfoo_とし、アクセス関数名をfoo()とする。設定関数が必要な場合はset_foo()とする。例外として、static constのデータメンバはprivateにする必要はない。名前はkFooとすること。

通常、アクセス関数はヘッダファイルにインラインで定義すること。

「継承」「関数名」も参照のこと。

宣言の順序

クラス定義はpublic、protected、privateの順とすること。空のセクションは除くこと。各セクション内における宣言は、一般的に以下の順序とすること。

  • 定数(static constデータメンバ)
  • コンストラクタ
  • デストラクタ
  • メソッド(スタティックメソッドを含む)
  • データメンバ(static constデータメンバを除く)

フレンド宣言は常にprivateとすること。DISALLOW_COPY_AND_ASSIGNマクロを使ってコピーと代入を無効にする場合は、privateセクションの最後(クラスの最後)に置くこと。「コピー/ムーブ可能な型」を参照。

出来る限り .ccファイルにおけるメソッド定義の順番は、宣言の順番と同じにすること。

大きなメソッド定義をクラス定義にインラインで書かないこと。詳細は「インライン関数」を参照のこと。

短い関数を書く

関数は小さく集中的なものが望ましい。

関数が40行を超えたら、関数が分割できないか考えてみること。関数を短くシンプルにキープしておくことで、他人がコードを読んだり修正したりすることが容易となる。

前へ | 目次 | 次へ

スコープ

このドキュメントは、Google C++ Style Guideを翻訳、要約したものです。省略した部分や意訳した部分が多くあります。原文の意図が伝わるよう注意しましたが、誤りが含まれているかもしれません。正確な情報については、必ず原文を参照してください。

前へ | 目次 | 次へ

スコープ

名前空間

.ccファイルでは名前なし名前空間の使用を推奨する。名前付き名前空間の名前は、プロジェクト名と、可能ならパス名に基づいたものとする。usingディレクティブ、インライン名前空間は使用禁止。以下に名前空間の使用のポリシーを示す。

名前なし名前空間

  • 名前衝突を避けるため .ccファイルでは名前なし名前空間の使用を推奨する。
  • // .ccファイル
    namespace {
    // この関数は、リンク時に他のシンボルと衝突しないことが保証される。
    // この関数は、この.ccファイルの中からしか見えない。
    bool UpdateInternals(Frobber* f, int newval) {
       ...
    }
    }  // namespace コメントで終端すること
    

    特定のクラスに関連するスコープは、クラスのスタティックデータメンバやスタティックメンバ関数、型として宣言したほうがよい。

  • .hファイルでは名前なし名前空間を使用してはならない。

名前付き名前空間

  • インクルード、gflags定義と宣言、他の名前空間にあるクラスの前方宣言を記述した後、残りのソースファイル全体を名前空間でラップする。
  • // .hファイル
    namespace mynamespace {
    // 名前空間スコープの中にすべての宣言を記述
    class MyClass {
    public:
      ...
      void Foo();
    };
    }  // namespace mynamespace
    
    // .ccファイル
    #include "a.h"
    
    DEFINE_bool(someflag, false, "dummy flag"); // gflagsの定義
    
    class C;  // グローバル名前空間のクラスCの前方宣言
    namespace a { class A; }  // a::Aの前方宣言
    
    namespace mynamespace {
    // 名前空間のスコープの中に関数の定義を記述
    void MyClass::Foo() {
      ...
    }
    }  // namespace mynamespace
    
  • 名前空間stdに何かを定義してはならない。標準ライブラリクラスの前方宣言も禁止。stdのエンティティを宣言する場合は、適切なヘッダをインクルードすること。
  • 名前空間のすべての名前を利用可能にするようなusingディレクティブは使用禁止。
  • // 禁止 - 名前空間を汚染する
    using namespace foo;
    
  • .ccファイル内と、.hファイルにおける関数、メソッド、クラスの中であれば、using宣言を使用してもよい。
  • using ::foo::bar;
    
  • ccファイル内、.hファイルの全体をラップする名前付き名前空間の中、関数、メソッドの中であれば、名前空間エイリアスを使用してもよい。
  • // .ccファイルで使うエイリアス
    namespace fbz = ::foo::bar::baz;
    
    // .hファイルで使うエイリアス
    namespace librarian {
    // このエイリアスは、このヘッダをインクルードするすべてのファイルで
    // 利用できる(名前空間librarianの中)。
    // エイリアス名はプロジェクト内で一貫したものを選ぶこと。
    namespace pd_s = ::pipeline_diagnostics::sidetable;
    
    inline void my_inline_function() {
        // 関数(メソッド)内でだけ有効なエイリアス
        namespace fbz = ::foo::bar::baz;
       ...
    }
    }  // namespace librarian
    

    .hファイルに書かれたエイリアスは、このファイルをインクルードするすべてのファイルから見えることに注意。そのため、パブリックヘッダ(プロジェクトの外で利用できるヘッダ)や、それらにインクルードされるヘッダではエイリアスを定義すべきでない。

  • インライン名前空間は使用禁止。

ネストされたクラス

パブリックなメンバクラス(クラス内で定義されたクラス)を作成してはならない。ただし、メンバクラスがインタフェースの一部である場合(あるメソッドのオプションを保持するクラスなど)を除く。それ以外はグローバルスコープを避けるため、名前空間を使用すること。

class Foo {
 private:
 // メンバクラスBar
 class Bar {
  ...
 };
};

非メンバ関数、スタティックメンバ関数、グローバル関数

クラスインスタンスに紐づかない関数が必要な場合は、名前空間内の非メンバ関数や、スタティックメンバ関数を使用すること。グローバル関数は基本的に使用禁止。

非メンバ関数は外部の変数に依存してはならない。また名前空間に常に存在しなくてはならない。スタティック関数をグループ化する場合は名前空間を使うこと。関数間でスタティックデータを共有する必要がある場合はクラスを作成すること。

.ccファイル内でだけ利用できる非メンバ関数が必要な場合は、名前なし名前空間を使用するかスタティックリンク(例:static int Foo() {…})を使用すること。

ローカル変数

変数はできるだけ狭いスコープに置くこと。最初に使用する場所のできるだけ近くで宣言し、宣言時に初期化すること。

if文やwhile文、for文で使用する変数は、文の中で宣言すること。ただし、変数がオブジェクトの場合はループの外側に宣言したほうが効率的である。スコープに入るたびにコンストラクタが呼び出され、スコープを出るたびにデストラクタが呼び出されてしまう。

// 効率的ではない実装:
for (int i = 0; i < 1000000; ++i) {
  Foo f;  // コンストラクタ、デストラクタが1000000回呼ばれる
  f.DoSomething(i);
}
Foo f;  // コンストラクタ、デストラクタは一回だけ呼ばれる
for (int i = 0; i < 1000000; ++i) {
  f.DoSomething(i);
}

スタティック変数とグローバル変数

クラス型のスタティック変数やグローバル変数は使用禁止。生成と解放の順序が不確定で、発見困難なバグを引き起こすため。constexpr宣言された変数は使用してもよい。

静的記憶期間を持つオブジェクト(グローバル変数、スタティック変数、スタティッククラスメンバ変数、関数スタティック変数)は、Plain Old Data (POD)でなくてはならない。PODとは、int型、float型、ポインタ型、PODの配列もしくは構造体。vector(代わりにCの配列を使うこと)やstring(const char []を使うこと)は使用してはならない。

クラス型のスタティック変数は、ビルドするたびにコンストラクタや初期化子の呼び出し順序が変わることさえある。そのため、クラス型のグローバル変数に加えて、関数の結果で初期化されるようなスタティックPOD変数も使用禁止。他のグローバルに依存しない関数(getenv()やgetpid()など)は除く。なお、この禁止ルールは関数スコープ内のスタティック変数には適用されない。それらの初期化順序は明確に定義されているため。

クラス型のスタティック/グローバル変数が必要なときは、main() 関数かpthread_once()でポインタ(freeされないポインタ)を初期化する。これは「スマート」なポインタではなく、ただのポインタとすること。スマートポインタは、解放の順序が不確定となる。

前へ | 目次 | 次へ

ヘッダファイル

このドキュメントは、Google C++ Style Guideを翻訳、要約したものです。省略した部分や意訳した部分が多くあります。原文の意図が伝わるよう注意しましたが、誤りが含まれているかもしれません。正確な情報については、必ず原文を参照してください。

前へ | 目次 | 次へ

ヘッダファイル

すべての .ccファイルに対して、対応する .hファイルを用意すること。ただし、ユニットテストや小さな .ccファイル(main関数のみなど)は除く。

自己完結したヘッダ

ヘッダファイルの拡張子は .hとすること。ヘッダではないテキストファイルは .incとすること。–inl.hヘッダは分割してはならない。

ヘッダは自己完結とすること。ヘッダをインクルードする際に特別な条件を要求してはならない。ヘッダは、他の必要なヘッダをすべてインクルードし、特定のシンボルが定義されていることを仮定してはならない。

テンプレートやインライン関数を .hファイルに宣言する場合は、同じファイルに定義も書くこと。また、これらの関数を使用するすべての .ccファイルでこのヘッダをインクルードすること。これらの定義を別々の-inl.hファイルに移動してはならない。

例外として、関数テンプレートがクラスのprivateメンバである場合や、明示的にインスタンス化される場合は、テンプレートをインスタンス化している .ccファイルに定義を記述してもよい。

#defineガード

多重インクルードを防ぐため、すべてのヘッダに #defineガードを設けること。シンボル名の形式は、<PROJECT>_<PATH>_<FILE>_H_ とすること。

例えば、fooプロジェクトのfoo/src/bar/baz.hの場合は以下のようになる。

#ifndef FOO_BAR_BAZ_H_
#define FOO_BAR_BAZ_H_
...
#endif  // FOO_BAR_BAZ_H_

前方宣言

不要なファイルオープンや再コンパイルを避けるため、前方宣言を使用すること。

コードで使用されるシンボルは、#includeから前方宣言に置き換えられる場合が多い。置き換えの指針は以下の通り。

  • ヘッダ内で宣言されている関数を使う場合は常にヘッダを #includeすること。
  • クラステンプレートを使う場合はそのヘッダを #includeした方がよい。
  • 通常クラスは前方宣言を使用してもよいが、前方宣言では不十分な場合があるので注意すること。不確かな場合は #includeを使うこと。
  • #includeを避けるためだけに、データメンバをポインタに置き換えることは禁止。

インライン関数

関数が小さいとき(10行以下)のみインライン関数にすること。10行を超える関数や、ループやスイッチ文がある関数をインライン化するコストメリットは小さい。

デストラクタに注意すること。暗黙的なメンバや基本クラスのデストラクタは見た目以上に行数が多い場合がある。また、仮想関数や再帰関数はインライン宣言してもインライン化されない。

関数パラメータの順序

パラメータは、入力、出力の順とすること。

これは厳格なルールではない。入力でも出力でもあるパラメータがあるため。また、関連する関数群に一貫性を持たせるためにルールを曲げてもよい。

名前とインクルードの順序

ヘッダファイルのインクルード順序は、その .ccの対応ヘッダ、Cライブラリ、C++ライブラリ、他ライブラリのヘッダ、プロジェクトのヘッダとすること。各セクション内では、アルファベット順に並べること。可読性が高まり、依存関係が明確となる。

例外として、条件付きインクルードは、他のインクルードの後に置いてもよい。

ヘッダファイルは、そのプロジェクトのソースディレクトリ配下に置くこと。ディレクトリの省略形である .(カレントディレクトリ)や ..(親ディレクトリ)は使用しない。

例えば、project/src/foo/internal/fooserver.ccのインクルードは以下のようになる。

#include "foo/server/fooserver.h"

#include <sys/types.h>
#include <unistd.h>
#include <hash_map>
#include <vector>

#include "base/basictypes.h"
#include "base/commandlineflags.h"
#include "foo/server/bar.h"

前へ | 目次 | 次へ

背景

このドキュメントは、Google C++ Style Guideを翻訳、要約したものです。省略した部分や意訳した部分が多くあります。原文の意図が伝わるよう注意しましたが、誤りが含まれているかもしれません。正確な情報については、必ず原文を参照してください。

前へ | 目次 | 次へ

背景

本ガイドの目的は、C++言語の特徴を生産的に利用しつつ、コードの管理を容易にすることにあります。

コードを管理する方法のひとつは、一貫性を強制することです。統一されたスタイルに従うことで、シンボルの意味をパターンマッチングで推測することが容易となります。共通のイディオムやパターンを用意しておくことで、コードが格段に理解しやすくなります。

C++は先進的な機能を多く備えていますが、本ガイドでは、特定の機能の使用を制限あるいは禁止しています。これは、コードをシンプルに保ち、その機能に起因するエラーや問題を避けるためです。

Googleのオープンソースプロジェクトはこのガイドに従っています。

前へ | 目次 | 次へ

Google C++スタイルガイド(要約版)

Google C++スタイルガイド(要約版)

このドキュメントは、Google C++ Style Guideを翻訳、要約したものです。省略した部分や意訳した部分が多くあります。原文の意図が伝わるよう注意しましたが、誤りが含まれているかもしれません。正確な情報については、必ず原文を参照してください。

[原文:http://google-styleguide.googlecode.com/svn/trunk/cppguide.html]

« 2015年5月 | トップページ | 2016年1月 »