フリーソフト

技術文書

終わりに

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

前へ | 目次 | 次へ

終わりに

常識を働かせよ、一貫性を持て。

コードを編集するときは、周りのコードを見渡してスタイルを確認すること。if節の周りにスペースがあったら、あなたもそうすべきであるし、コメントがアスタリスクで囲まれたボックスになっていたら、あなたもそのようにコメントを書くべきである。

スタイルガイドはコーディングにおけるグローバルなルールであるが、ローカルなスタイルに従うことも重要である。周りのコードと大幅にスタイルが異なるコードを書き加えると、コードを理解することが難しくなる。スタイルの不連続は避けなくてはならない。

楽しんでコードを書こう!

前へ | 目次 | 次へ

ルールの例外

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

前へ | 目次 | 次へ

ルールの例外

ルールに準拠していない既存のコード

このスタイルガイドに準拠していないコードを扱うときは、既存のコードのスタイルとの一貫性を維持するためにルールから逸れてもよい。

Windowsのコード

Windowsには、WindowsヘッダやMicrosoftのコードに由来する独自のコーディングスタイルが存在する。しかしながら、皆が理解しやすいコードを書くためには、あらゆるプラットフォームに共通のガイドラインに従うことが重要である。

Windowsプログラマが注意すべき点を以下に示す。

  • ハンガリアン記法(例:整数の名前をiNumとする)は使用禁止。Googleの命名規則に従うこと。ソースファイルの拡張子も .ccとすること。
  • Windowsは基本型に対する独自の別名を多く定義している(DWORD、HANDLEなど)。Windows API関数を呼び出す場合は、これらの別名の使用を推奨する。ただし、できるだけC++の基本型に近いものを使用するようにすること。例えば、LPCTSTRの代わりにconst TCHAR * を使用すること。
  • Microsoft Visual C++でコンパイルする際は、コンパイラの警告レベルを3以上に設定し、すべての警告をエラーとして扱うこと。
  • #pragma once; は使用禁止。代わりにGoogleのインクルードガードを使用すること。インクルードガードのシンボル名のパスはプロジェクトツリーのトップからの相対パスとすること。
  • どうしても使用せざるを得ない場合を除き、非標準な拡張はすべて使用禁止(#pragmaや__declspecなど)。__declspec(dllimport) と__declspec(dllexport) は使用してもよい。ただし、DLLIMPORTとDLLEXPORTのようなマクロを通じて使用すること。コードを共有する際にそれらの拡張を簡単に無効にできるようにするため。

Windowsにおける例外を以下に示す。

  • COMやATL/WTLクラス、インタフェースを実装する際には多重継承を使用してもよい。
  • ATLやいくつかのSTL(Visual C++に付属しているものを含む)では例外が広く使用されている。ATLを使用する際は、_ATL_NO_EXCEPTIONSをdefineして例外を無効にすること。STLの例外を無効する方法も調べなくてはならない。無効にできない場合は、コンパイラで例外をONにしてもよい。(これはSTLをコンパイルするためだけである点に注意。例外をハンドルするコードを自分自身で書いてはならない。)
  • プリコンパイル済みヘッダを使用する場合は、それらを自動的にインクルードするためのコンパイラオプション/FIを使用すること。ソースファイルの先頭で特定のヘッダ(StdAfx.hやprecompile.hのような名前)を明示的にインクルードする方法はとらないこと。他のプロジェクトとコードの共有が困難となる。
  • リソースヘッダ(通常 resource.hという名前でマクロのみを含む)は、スタイルガイドに従う必要はない。

前へ | 目次 | 次へ

書式

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

前へ | 目次 | 次へ

書式

コーディングのスタイルは好みによるところが大きいが、全員が同じスタイルに従うことで、誰が書いたコードでも簡単に理解することが可能となる。

以下からコードを書式化するためのemacs用の設定ファイルが入手できる。
http://google-styleguide.googlecode.com/svn/trunk/google-c-style.el

 

行の長さ

テキストの各行は最大で80文字とすること。

例外:

  • コメント行に書いたコマンド例やURLリテラルが80文字を超える場合。カット&ペーストが簡単にできるように途中で改行しなくてもよい。
  • raw-stringリテラルが80文字を超える場合。テストコードを除き、このようなリテラルはファイルの先頭付近に置くこと。
  • #include文が長いパスを持つ場合。
  • ヘッダガードが80文字を超える場合。

非ASCII文字

非ASCII文字は基本的に使用してはならない。使用する場合はUTF-8形式を使用すること。

ユーザに表示するテキストをハードコードしてはならない(英語でもだめ)。コードに非ASCII文字が含まれることはめったにないはずである。非ASCII文字をハードコードせざるを得ない場合(例えば、外部のデータファイルを読み込むコードで、非ASCIIの区切り文字をハードコードする場合など)は、多くのツールが認識できるUTF-8を使うこと。

16進数エンコードを使用してもよい。特に読みやすさが向上するような場合に推奨される。例えば、"\xEF\xBB\xBF"(もっとシンプルにu8"\uFEFF")はUnicodeの長さ0、改行なしの空白文字を表すが、UTF8としてソースに含まれていたら目視できない。

エスケープシーケンス\uXXXXを含む文字列リテラルがUTF-8でエンコードされていることを保証するには、u8接頭辞を使用すること。UTF-8でエンコードされた非ASCII文字を含む文字列には使用しないこと。これは、コンパイラがソースファイルをUTF-8として解釈できない場合に、不正な出力を生成するため。

C+11のchar16_tとchar32_t文字型は使用してはならない。これらは非UTF-8テキストのためのものではない。同じ理由でwchar_tを使うべきでない(wchar_tを広範囲にわたって使用するWindows APIとやりとりするコードは除く)

スペース vs. タブ

インデントはスペース2つとすること。タブは使用しないこと。

関数宣言と定義

関数名と同じ行に戻り型を書くこと。1行に収まる場合は同じ行にパラメータも書くこと。

ReturnType ClassName::FunctionName(Type name1, Type name2) {
  DoSomething();
  ...
}
// 1行に収まらない場合
ReturnType ClassName:: FunctionName(Type name1, Type name2,
                                    Type par_name3) {
  ...
}
// 1つ目のパラメータから収まらない場合
ReturnType LongClassName::ReallyReallyLongFunctionName(
    Type par_name1,  // インデントはスペース4つ
    Type par_name2,
    Type par_name3) {
  DoSomething();  // インデントはスペース2つ
}

注意すべきポイント

  • 戻り型と関数名が1行に収まらない場合は、それらの間で改行すること。
  • 戻り型の後ろで改行した場合は、インデントしないこと。
  • 開き丸かっこは常に関数名と同じ行に書くこと。
  • 関数名と開き丸かっこの間にはスペースを書かないこと。
  • 丸かっことパラメータの間にスペースを書かないこと。
  • 開き中括弧は最後のパラメータと同じ行に書くこと。
  • 閉じ中括弧は関数の最後の行に書くか、開き中括弧と同じ行に書くこと(このスタイルが許可されている場合)。
  • 閉じ丸括弧と開き中括弧の間にはスペースを書くこと。
  • すべてのパラメータには名前を付け、宣言と実装において同じ名前とすること。
  • 可能なら、すべてのパラメータを整列させること。
  • デフォルトのインデントはスペース2つ。
  • 括られたパラメータのインデントはスペース4つ。

使用していないパラメータがある場合は、関数宣言において変数名をコメントアウトする。変数名を消してしまうと、後で変数の意味が分からなくなってしまう。

void Circle::Rotate(double /*radians*/) {}

ラムダ式

ラムダ式のパラメータや本体は他の関数と同じような形式で書くこと。キャプチャリストは他のカンマ区切りリストと同じように書くこと。

参照キャプチャについては、アンパサンド(&)と変数名の間にスペースを入れないこと。

int x = 0;
auto add_to_x = [&x](int n) { x += n; };

短いラムダは、関数の引数にインラインで記述してもよい。

関数呼び出し

関数呼び出しは、1行で書くこと。

1行に収まらない場合は、引数の途中で改行し、インデントを最初の引数に合わせる。開き丸括弧 ‘(‘ の後ろや、閉じ丸括弧 ‘)’ の前にスペースを入れてはならない。開き丸括弧 ‘(‘ の後ろで改行し、引数を別の行から書き始めてもよい(インデントはスペース4つ)。いずれの場合も、特別な理由がない限り、複数の引数を同じ行に置くなどして行数を最小にとどめること。

bool retval = DoSomething(averyveryveryverylongargument1,
                          argument2, argument3);

DoSomething(
    argument1, argument2,  // 4 space indent
    argument3, argument4);

中括弧初期化リストの書式

関数呼び出しの書式と同じ。

名前(型名や変数名)の後ろにリストが続く場合は、{ } を関数呼び出しの丸括弧と見なしてフォーマットする。名前がない場合は、名前の長さが0と仮定する。

// 1行で書く例
return {foo, bar};
functioncall({foo, bar});
pair

p{foo, bar}; // 複数行で書く例 SomeFunction(     {"assume a zero-length name before {"},     some_other_function_parameter); SomeType variable{     "This is too long to fit all in one line"}; MyType m = {  // { の前で改行してもよい     superlongvariablename1,     {short, interior, list},     {interiorwrappinglist,     interiorwrappinglist2}};

条件文

条件文の丸括弧内にはスペースを書かないことを推奨。ifとelseキーワードは別の行に書くこと。

条件文の書式は2種類。丸括弧の内側にスペースを入れるものと、入れないもの。

if (condition) {  // 丸括弧内にスペースなし
  ...  // インデントはスペース2つ
} else if (...) {  // 閉じ中括弧と同じ行にelse
  ...
} else {
  ...
}
if ( condition ) {  // 丸括弧内にスペースあり – まれな書き方
  ...
}

どちらの書式でもよいが一貫性をもつこと。既存ファイルを修正する場合は、すでに使用されている書式に従うこと。また、新しいコードを書く場合は、プロジェクト内の他のファイルの書式を確認し、それに従うこと。判別がつかず、かつ個人的な好みがない場合は、スペースなしの書式にすること。

if と ‘(‘ の間にスペースを入れること。 ‘)’ と ‘{‘ の間にもスペースを入れること。

if(condition) {   // だめ - IFの後にスペースがない
if (condition){   // だめ - { の前にスペースがない
if(condition){    // だめだめ

読みやすさのため短い条件文を1行に書いてもよい。ただしelse節がある場合は除く。

if (x == kFoo) return new Foo();
if (x == kBar) return new Bar();

// だめ - ELSE節があるのにIF文が1行になっている
if (x) DoThis();
else DoThat();

構文上、文が1つの場合には中括弧は不要であるが、好みに応じて中括弧を書いてもよい。常に中括弧を書くというスタイルのプロジェクトもある。

if-else文で中括弧を使用する場合は、他の部分でも使わなくてはならない。

// だめ - IFには中括弧があるがELSEにはない
if (condition) {
  foo;
} else
  bar;

// だめ - ELSEに中括弧があるがIFにはない
if (condition)
  foo;
else {
  bar;
}

ループとスイッチ文

switch文のブロックに中括弧を使用してもよい。非自明なcaseの通り抜け(breakなしのcase)にはコメントをつけること。ループのボディが1文の場合、中括弧は任意。空のループのボディは {} かcontinueとすること。

switch文のcaseブロックに中括弧を書く場合は、以下のように書くこと。

switch (var) {
  case 0: {  // スペース2つでインデント
    ...      // スペース4つでインデント
    break;
  }
  ...
  default: {
    assert(false);
  }
}

switch文の変数が列挙値でない場合はdefault caseを必ず書くこと(列挙値の場合は未処理の値があるとコンパイラが警告を発してくれる)。default caseが実行されないはずの場合は、単純にassertを置くこと。

空ループの本体には{}あるいはcontinueを使用すること。セミコロンのみは禁止。

while (condition) {
  // falseを返すまでテストを継続
}
for (int i = 0; i < kSomeNumber; ++i) {}  // 空のボディ
while (condition) continue;  // continue
// だめ – do/whileループの一部に見える
while (condition);  

ポインタと参照表現

ピリオドや矢印の前後にスペースを入れてはならない。ポインタオペレータ(*, &)の後ろにスペースを入れてはならない。

x = *p;
p = &x;
x = r.y;
x = r->y;

ポインタ変数やポインタ引数の宣言では、型名か変数名のどちらかにポインタオペレータを隣接させる。

// OK - 変数名にポインタオペレータが隣接
char *c;
const string &str;

// OK - 型名にポインタオペレータが隣接
char* c;  // 複数の場合は "char* c, *d, *e, ...;" とする!
const string& str;
char * c;  // だめ - * の両側にスペース
const string & str;  // だめ - & の両側にスペース

ファイル内で一貫性を持つこと。既存ファイルを修正する場合は、既存のコードのスタイルに合わせること。

論理式

論理式が1行に収まらない場合は、一貫性を持って行を分割すること。

Googleのコードでは論理オペレータを行末に置くスタイルが一般的。

if (this_one_thing > this_other_thing &&
     a_third_thing == a_fourth_thing &&
     yet_another && last_one) {
  ...
}

オペレータを行の始めに置いてもよい。可読性を高めるために丸括弧を追加してもよい。and や cmplのような単語オペレータは使用せず、常に && や ~ のような記号オペレータを使用すること。

戻り値

必要以上にreturn式を丸括弧で囲んではならない。

// 複雑な式を読みやすくするための丸括弧はOK
return (some_long_condition && another_condition);
// だめな例
return (value);  // var = (value); とは書かないよね
return(result);  // return は関数ではない!

変数と配列の初期化

初期化には = あるいは ( )、{ } が使用できる。

// 全部OK
int x = 3;
int x(3);
int x{3};
string name = "Some Name";
string name("Some Name");
string name{"Some Name"};

中括弧の初期化リストをstd::initializer_listで使用するときは注意。

vector

v(100, 1);  // 100個の1を持つベクトル vector

v{100, 1};  // 100と1を持つベクトル

プリプロセッサ命令

プリプロセッサ命令のシャープ記号は、常に行の先頭に書くこと。

  if (lopsided_score) {
#if DISASTER_PENDING  // 正しい
    DropEverything();
# if NOTIFY  //正しい、ただし#の後ろのスペースは必須ではない
    NotifyClient();
# endif
#endif
    BackToNormal();
  }
// だめ - シャープ記号がインデントされている
  if (lopsided_score) {
    #if DISASTER_PENDING  // だめ!
    DropEverything();
    #endif                // だめ!
    BackToNormal();
  }

クラスの書式

セクションはpublic、protected、private順に書き、各キーワードはスペース1つでインデントすること。

クラス宣言の基本的な書式(コメントは省略)は以下の通り。

class MyClass : public OtherClass {
 public:      // 注意:スペース1つでインデント!
  MyClass();  // スペース2つでインデント
  explicit MyClass(int var);
  ~MyClass() {}

  void SomeFunction();
  void SomeFunctionThatDoesNothing() {
  }

  void set_some_var(int var) { some_var_ = var; }
  int some_var() const { return some_var_; }

 private:
  bool SomeInternalFunction();

  int some_var_;
  int some_other_var_;
};

注意点:

  • 基本クラス名はサブクラス名と同じ行に書く(80文字に収まる場合)。
  • public:、protected:、private:キーワードはスペース1つでインデント。
  • これらのキーワードの前は空行にする(最初のキーワードは除く)。このルールは小さなクラスでは任意。
  • キーワードの後ろは空行にしないこと。
  • セクションは、public、protected、privateの順に書く。
  • 各セクション内での宣言順序については「宣言の順序」を参照のこと。

コンストラクタ初期化子リスト

コンストラクタの初期化子リストの書き方は次の2種類。

// 1行に収まるとき
MyClass::MyClass(int var) : some_var_(var) {}

もしくは

// 複数行が必要なとき
// スペース4つでインデント、1番目の初期化子の行にコロン
MyClass::MyClass(int var)
    : some_var_(var),  // スペース4つでインデント
      some_other_var_(var + 1) {
  ...
}

名前空間の書式

名前空間のコンテンツはインデントしてはならない。

namespace {
  // 間違い、インデントしてはならない
  void foo() {
    ...
  }
}  // namespace

ネストされた名前空間を宣言する場合は、それぞれを1行に書くこと。

namespace foo {
namespace bar {

水平方向の空白

以下のルールに従って水平方向の空白を使用すること。行末に空白を入れてはならない。

一般

void f(bool b) {  // 開き中括弧 ‘{‘ の前にスペースを入れる
  ...
int i = 0;  // セミコロンの前はスペース不要
// 初期化リストの中括弧内のスペースは任意
// 入れる場合は両側に入れること!
int x[] = { 0 };
int x[] = {0};
// 継承と初期化リストのコロンの両側にはスペースを入れる
class Foo : public Bar {
 public:
  Foo(int b) : Bar(), baz_(b) {}  // 空の中括弧内はスペース不要
  void Reset() { baz_ = 0; }  // 中括弧と実装の間にスペース
  ...

ループと条件式

if (b) {  // 条件式・ループのキーワードの後ろにスペースを入れる
} else {  // elseの前後にスペースを入れる
}
while (test) {}  // 丸括弧内は通常スペース不要
switch (i) {
for (int i = 0; i < 5; ++i) {

// 丸括弧内にスペースを入れてもよいが、まれな書き方
switch ( i ) {
if ( test ) {
for ( int i = 0; i < 5; ++i ) {

// forループではセミコロンの後ろにスペースを入れる
// セミコロンの前にスペースを入れてもよいが、まれな書き方
for ( ; i < 5 ; ++i) {

// 範囲ベースのforループではコロンの前後にスペースを入れる
for (auto x : counts) {

switch (i) {
  case 1:         // コロンの前はスペース不要
    ...
  case 2: break;  // コードがある場合はコロンの後ろにスペース

演算子

// 代入演算子の前後にはスペースを入れる
x = 0;

// 2項演算子の前後にはスペース、係数のスペースはなくてもOK
// 丸括弧とコンテンツの間はスペース不要
v = w * x + y / z;
v = w*x + y/z;
v = w * (x + z);

// 単項演算子と引数の間はスペース不要
x = -5;
++x;
if (x && !y)

テンプレートとキャスト

// 角括弧の内側にはスペース不要
// キャストの ‘<‘ の前や、’>(‘ の間にはスペース不要
vector

x; y = static_cast

(x); // 型名とポインタの間のスペースはOK、一貫性を持つこと vector

x; set

> x;  // C++11では許される書き方 set

> x; // C++03では ‘> >‘ にスペースが必要 // 対称に ‘< <‘ にスペースを入れてもよい set< list

> x;

垂直方向の空白

垂直方向の空白(空行)は最小限にすること。

これはルールというよりも原則である。具体的には、関数と関数の間に2もしくは3行以上の空行を入れてはならない。関数の先頭や最後を空行にしてはならない。関数内での空行の使用も制限しなくてはならない。

1画面に多くのコードを表示するとプログラムの流れが理解しやすくなる。もちろん、詰まり過ぎたコードは散らばりすぎたコードと同じくらい読みにくい。うまく調整すること。

空行が有用かどうか判断する際の経験則

  • 関数の先頭や最後にある空行が可読性を高めることはない
  • 一連のif-elseブロック内の空行は可読性を高める

前へ | 目次 | 次へ

コメント

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

前へ | 目次 | 次へ

コメント

コードを読みやすくキープするためにはコメントは必須である。ここでは何をどこにコメントすべきかを記述する。しかし、最良のコメントとはコード自身がドキュメントになっているものである。コメントで説明しなくてはならないあいまいな変数名を使用するよりも、分かりやすい名前を付ける方がはるかによい。

コメントは、次の貢献者のことを考えて書くこと。寛大であれ。次の貢献者はあなた自身かもしれない。

コメントのスタイル

// もしくは /* */ 構文を使用する。// がより一般的である。コメントの内容や、どの構文をどこで使うかなど、一貫性を保つこと。

ファイルのコメント

各ファイルはライセンスの決まり文句から始め、そのあとにファイルの説明を書くこと。

法律上の注意事項と著者表示

すべてのファイルにライセンスの常用文を含めること。プロジェクトで使用するライセンス(例:Apache 2.0、BSD、LGPL、GPL)に適した常用文を選択すること。ファイルを大幅に変更する場合は、著者表示の削除を検討すること。

ファイル内容

ファイル内容を説明するコメントをファイルの上部に必ず書くこと。

通常、.hファイルにはクラスの目的や使用方法の概要を記述する。.ccファイルには、実装の詳細やトリッキーなアルゴリズムに関する議論といった詳細な情報を記述する。.ccではなく .hに詳細な情報を書いた方がよいと考えられる場合はそうしてもよい。ただしその場合は、.hにドキュメントがあることを .ccファイルに書かなくてはならない。また、.hと .ccに重複したコメントを書いてはならない。

クラスのコメント

クラス定義のコメントには、クラスの目的と使用方法を記述すること。

ファイルの先頭にクラスの詳細なコメントが記述済みの場合は、単純に「詳しい説明はファイルの先頭を参照のこと」と書いておく。必ず何かのコメントは書いておくこと。

クラスの同期処理の仮定をドキュメント化すること。クラスのインスタンスが複数のスレッドからアクセスできる場合は、利用ルールとマルチスレッド処理における不変量について注意してドキュメント化すること。

関数のコメント

関数の宣言部分のコメントには、関数の使い方を記述すること。関数の定義部分のコメントには、関数の処理内容を記述すること。

関数宣言

関数宣言の直前にコメントを記述すること。コメントには、関数の目的と使い方を書くこと。これらのコメントは、命令的("Open the file”)ではなく記述的(”Opens the file”)にすること。一般的に、ここでは関数の処理内容については記述しない。

関数宣言のコメントに記述すべき内容は以下のとおり。

  • 入力が何で、出力が何であるか。
  • クラスのメンバ関数について:メソッド呼び出し後も参照引数を覚えているのか、それらをfreeするのかしないのか。
  • 関数がメモリを割り当てる場合、呼び出し側で解放する必要があるのか。
  • 引数にNULLポインタを渡すことができるのか。
  • 関数の使い方によってパフォーマンスに影響がでるのか。
  • 関数は再入可能なのか。同期の仮定は何か。

自明なコメントは省略すること。デストラクタにヘッダのコメントがないことはよくあること。「オブジェクトを破棄する」といったコメントは不要。

関数定義

何かしらトリッキーな処理がある場合は、関数定義のコメントで説明すること。処理ステップの概要や、なぜその方法で実装したのかについて説明する。

.hファイルと重複したコメントを記述しないこと。関数が何をするかについて簡単に要約することは問題ないが、コメントの重点はそれをどのようにやるかについてである。

変数のコメント

変数名は、その変数が何に使われるのかが分かるようなものにすること。必要に応じてコメントを書くこと。

クラスのデータメンバ

クラスのデータメンバ(インスタンス変数、メンバ変数)には、何のために使われるのかを説明するコメントをつけること。変数が特別な意味を持つ値(nullポインタや-1)をとる場合は説明を書くこと。

グローバル変数

データメンバと同様、その変数が何のために使用されるのかを説明するコメントをつけること。

実装のコメント

コードのトリッキーな部分、不明瞭な部分、興味深い部分、重要な部分にはコメントを書くこと。

説明のためのコメント

トリッキーなコードや複雑なコードには、それらの前にコメントを書くこと。

行コメント

不明瞭な行には行の後ろにコメントを書くこと。コードの行末とコメントの間は空白2つで区切ること。

nullptr/NULL, true/false, 1, 2, 3...

nullポインタや論理値、整数リテラルを関数に渡す場合は、それらが何であるかをコメントに記述すること。あるいは、定数を使用するなどしてコード自体に意味を持たせること。

やってはいけないこと

コード自体を説明してはならない(コードを見れば明らかなことはコメントしない)。

句読点、綴り、文法

句読点、綴り、文法に注意を払うこと。きちんと書かれているコメントほど読みやすい。

断片的な文よりも完全な文のほうが読みやすい。文末コメントのような短いコメントについては多少崩れてもよいが、スタイルに一貫性を持つこと。

TODOコメント

一時的なコード、短期的な解決策、十分であるが完全ではないコードに対しては、TODOコメントを使用すること。

TODOコメントは、すべて大文字の文字列「TODO」から始め、そのあとに名前やEメールアドレスなど、この問題の事情を一番よく知る人物の情報を書く。TODOの主目的は、より詳細な情報を獲得するための方法を提供することである。TODOに名前がある人に、問題を修正する責任があるという意味ではない。あなたがTODOコメントを書くときは、あなたの名前を書いておくこと。

// TODO(kl@gmail.com): 2005年11月までに修正

TODOが「将来何かをする」というような内容の場合は、具体的な日付を書くか(例:2005年11月までに修正)、具体的なイベントを書くこと(例:全クライアントがXMLレスポンスを処理できた時点でこのコードを取り除く)。

非推奨コメント

非推奨のインタフェースにはDEPRECATEDコメントをつけること。

DEPRECATEDコメントは、すべて大文字の単語「DEPRECATED」から始め、そのあとに丸かっこで名前やEメールアドレスなどを書く。コメントは、インタフェース宣言の前か、同じ行に書く。

呼び出し側のコードを修正する人のために、DEPRECATEDコメントはシンプルで明確な指示とすること。非推奨関数を、新しいインタフェースを呼び出すようなインライン関数として実装してもよい。

新規のコードは非推奨インタフェースの呼び出しを含んではならない。代わりに新しいインタフェースを使用すること。指示が理解できない場合は、DEPRECATEDコメントを書いたい人に確認すること。

前へ | 目次 | 次へ

命名規則

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

前へ | 目次 | 次へ

命名規則

命名規則は一貫性を保つための最も重要なルールである。名前を見るだけで、それが型なのか、変数なのか、関数なのか、定数なのか、マクロなのか、といった情報を即座に伝えることができる。

命名規則は好みによる部分が大きいが、個人の好みよりも一貫性が重要と考える。理にかなっているかどうかではなく、ルールはルールと考えること。

一般的な命名規則

関数名、変数名、ファイル名は記述的に書くこと。省略は避けること。

できるだけ記述的な名前をつけること。名前が長くなってもよい。即座に理解できることの方がはるかに重要である。あいまいな省略形や他のプロジェクトのメンバになじみのない省略形を使用しないこと。単語の途中の文字を削除するような省略形を使用しないこと。

ファイル名

ファイル名はすべて小文字とする。アンダースコア(_)もしくはダッシュ(-)を含んでもよい。どちらを使うかはプロジェクトのルールに従うこと。ルールがない場合はアンダースコアを使うとよい。

C++ファイル名は .ccで終わり、ヘッダライル名は .hで終わること。特定の箇所でインクルードされるようなテキストファイルの名前は .incで終わること。

/usr/includeに存在するファイル名は使用しないこと(例:db.h)。
*_unittest.cc や *_regtest.ccといったファイル名も非推奨。

ファイル名は具体的なものにすること。例えば、logs.hよりもhttp_server_logs.hとした方がよい。クラス名などに基づいたファイル名がよく用いられる。例えば、FooBarクラスを定義するfoo_bar.hとfoo_bar.cc。

「自己完結したヘッダ」も参照のこと。

型名

型名は先頭を大文字とし、各単語の先頭を大文字にする。アンダースコアは使用しない。
例:MyExcitingClass、MyExcitingEnum。

変数名

変数名、データメンバ名はすべて小文字とし、単語間はアンダースコアでつなぐ。クラス(構造体は除く)のデータメンバは名前の最後にアンダースコアをつける。
例:a_local_variable、a_struct_data_member、a_class_data_member_。

グローバル変数は他の変数と簡単に区別できるようg_ などの接頭辞をつけた方がよい。

定数名

定数名は先頭をkとし、各単語の先頭を大文字にする。例:kDaysInAWeek。

ローカルスコープ内の定数は、定数の命名規則に従ってもよいし、通常の変数の命名規則に従ってもよい。

関数名

関数名は先頭を大文字とし、各単語の先頭を大文字にする。アンダースコアは使用しない。アクセッサやミューテータについては変数名の命名規則に従う。
例:MyFunction()、MyMethod()、my_ member_variable()、set_my_member_variable()。

通常の関数

エラーでクラッシュする関数は、関数名の終わりにOrDieを追加する。この規則は、生成コードにおいて使用される関数であり、かつ通常の操作でエラーが発生しうる場合にのみ適用される。例:AddTableEntry()、DeleteUrl()、OpenFileOrDie()

アクセッサとミューテータ

アクセッサとミューテータ(getとset関数)の名前は、対象の変数名に対応するようにつける。例:num_entries_ に対してint num_entries() とvoid set_num_entries(int num_entries)。

非常に短いインライン関数についても小文字名を使用してもよい。

名前空間名

名前空間名はすべて小文字とし、プロジェクト名に基づく名前とする。可能ならディレクトリ構造も反映した名前とする。例:google_awesome_project。

列挙子名

列挙子とはenumの個々の項目を指す。列挙子名は定数名あるいはマクロ名の命名規則に従う。可能なら定数名の命名規則に従うことが望ましい。例:kErrorOutOfMemory = 1あるいはOUT_OF_MEMORY = 1。

マクロ名

通常、マクロは使用すべきでない(「プリプロセッサのマクロ」を参照)。マクロ名はすべて大文字とし、アンダースコアを使用する。例:ROUND、PI_ROUNDED。

命名規則の例外

CやC++にすでに存在する項目と類似したものに名前を付ける場合は、既存の命名規則に従ってもよい。

前へ | 目次 | 次へ

その他の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"

前へ | 目次 | 次へ