Revision 3.199

Benjy Weinberger
Craig Silverstein
Gregory Eitzmann
Mark Mentovai
Tashana Landray
(日本語訳 Takashi Sasai
このスタイルガイドには詳細情報がたくさん盛り込まれていますが、最初は表示されていません。左端にある矢印ボタンをクリックしてみてください。 「やったね」と表示されるはずです。

やったね! このように矢印ボタンをクリックすると詳細情報が表示されます。 このドキュメントの先頭にある大きな矢印ボタンをクリックしても構いません。そうすれば、すべての詳細情報が表示されます。

C++は主要な開発言語であり、Googleのオープンソースプロジェクトでも数多く使われています。 C++プログラマであればだれでも知っているように、C++には強力な機能がたくさんあります。ところが、その力ゆえに複雑でもあります。C++で書かれたコードはバグを生みやすく、読みにくく、メンテナンスがしにくくなるおそれがあるのです。

このスタイルガイドが目標にしているのは、C++でコードを書くときに「すべきこと」「すべきではないこと」を詳しく説明することで、C++にある複雑さを何とか改善することです。ここに挙げたルールはみな、C++の機能を活用して生産性を上げながらも、コードベースを管理しやすくするためのものです。

スタイルというのは読みやすさにも関係していますが、ここで言うところのスタイルはC++で書かれたコードに適用する規則を指しています。したがって、ここで「スタイル」という言葉を使うのは少々語弊があるかもしれません。ここに挙げた規則は、ソースコードの書式に限らないためです。

コードベースを管理しやすくする方法のひとつは、強制的に一貫性を持たせることです。どんなプログラマでもコードを見ればすぐに理解できる、これはとても重要なことです。 全員が一貫性のあるスタイルを守って規則にしたがっていれば、それぞれのシンボルが何を意味しているのか、どの不変式が真なのかといったことが、簡単な「パターンマッチング」により推測できるようになります。よく使うイディオムやパターンを作っておくと、コードはとても理解しやすくなります。そうはいっても、スタイルを逸脱するのに十分な理由がある場合もあるでしょう。しかし、それでもなお、私たちは一貫性を保つことが重要だと考え、スタイルをそのまま適用することを選んでいます。

このスタイルガイドで解決しようとしているもうひとつの問題は、肥大化したC++の機能についてです。 C++は数多くの先進的な機能を備えた巨大な言語ですが、このガイドラインでは一部の機能の使用を制限している場合があります。なかには使用を禁止しているものまであります。これはコードをシンプルにすることで、こうした機能によって引き起こされる間違いや問題を避けるためです。このスタイルガイドではそうした機能を挙げるとともに、なぜ使用を制限したのか、その理由についても説明しています。

Googleが開発しているオープンソースプロジェクトは、このスタイルガイドにしたがっています。

これはC++のチュートリアルではないことに注意してください。読者はC++についてよく知っていることを想定しています。

一般的に、すべての .cc ファイルには、それに対応する .h ファイルを用意するべきです。ただしユニットテストのためのファイルや main() 関数1つだけの .cc ファイルなど、小さなファイルの場合にはその限りではありません。

ヘッダファイルを正しく使うことで、コードの読みやすさ、サイズ、パフォーマンスを大幅に改善できます。

以下のルールにしたがうと、ヘッダファイルを使うときの落とし穴を避けられます。

多重インクルードを防ぐため、ヘッダファイルには #define ガードを入れるべきです。また #define ガードのシンボル名は、 <PROJECT>_<PATH>_<FILE>_H_ という書式にするべきです。

シンボル名が確実にユニークなものになるよう、プロジェクトのソースツリーのフルパスに基づいたシンボル名を付けるべきです。たとえば foo というプロジェクトの foo/src/bar/baz.h というファイルの場合、次のような #define ガードにします。

#ifndef FOO_BAR_BAZ_H_ #define FOO_BAR_BAZ_H_ ... #endif // FOO_BAR_BAZ_H_
前方宣言で十分なときには #include を使ってはいけません。

ヘッダファイルをインクルードすると、そのヘッダファイルとの依存関係が生まれます。すると、そのヘッダファイルを変更するたびに再コンパイルが必要になります。あなたが書いたヘッダファイルの中で、他のヘッダファイルをインクルードしているとしましょう。このとき、あなたがインクルードしているヘッダファイルが変更されると、あなたのヘッダファイルをインクルードしているコードまで、すべて再コンパイルが必要になります。したがって、インクルードするヘッダファイルは最小限にしておきましょう。ヘッダファイルで他のヘッダファイルをインクルードするときには、特によく注意しましょう。

前方宣言を使うことで、ヘッダファイルにインクルードするヘッダファイルの数を大幅に削減できます。たとえばヘッダファイルで File クラスを使っていても、File クラスの宣言にアクセスする必要がなければ、ヘッダファイルに class File; と前方宣言しておくだけで十分です。#include "file/base/file.h" などとする必要はありません。

Foo クラスの定義にアクセスせずに、そのヘッダファイルで何ができるのでしょうか?

  • Foo* 型や Foo& 型のデータメンバを宣言できます。
  • Foo 型の引数や戻り値を持つ関数を宣言できます(定義ではない)。(ただし引数 Foo もしくは const Foo& が非explicitな引数1つのコンストラクタを持ち、自動型変換をサポートするために完全な定義を必要としている場合は例外です。)
  • Foo 型のスタティックデータメンバを宣言できます。スタティックデータメンバはクラス定義の外部に定義されるためです。

これに対して、書いているクラスが Foo のサブクラスであったり、そのクラスに Foo 型のデータメンバがある場合には、Foo のヘッダファイルをインクルードする必要があります。

オブジェクトではなくポインタ(scoped_ptr を使うのが望ましい)をメンバにする方がよい場合もあります。しかし、そうするとコードは読みにくくなり、パフォーマンスの面でも不利になります。ヘッダファイルのインクルードを最小限にすることだけのために、こうした置き換えをするのは避けましょう。

言うまでもないことですが、通常 .cc ファイルには利用するクラスの定義が必要になるので、ヘッダファイルをインクルードする必要があります。

もしソースファイルで Foo というシンボルを使っているなら、#include か前方宣言のいずれかによって Foo の定義を自分で引き込んでおくべきです。自分で直接インクルードしていないヘッダによって、そのシンボルが引き込まれることを当てにしてはいけません。ただし、myfile.ccFoo を使っていて、インクルードしている myfile.hFoo をインクルード(あるいは前方宣言)している場合は、その限りではありません。
インライン関数を定義するのは関数が小さいとき(10行以下)だけにしてください。 関数を宣言するとき、通常の関数呼び出しメカニズムで呼び出すのではなく、インライン関数として展開するようコンパイラに指示できます。 インライン化する関数が小さいと、コンパイラは効率のよいオブジェクトコードを生成してくれます。アクセサやミューテータ、短くてパフォーマンスに影響を及ぼす関数は、遠慮なくインライン化するべきです。 インライン化しすぎると、実際にはプログラムが遅くなってしまうことがあります。関数の大きさによってコードサイズが増減するためです。非常に小さなアクセサ関数であれば、インライン化することでコードサイズが減ります。ところが巨大な関数をインライン化すると、コードサイズが劇的に増えることがあります。最近のプロセッサであれば通常、コードが小さければ小さいほど、命令キャッシュの利用効率が上がって高速に実行できます。

インライン化すべきか否かについては、優れた経験則があります。それは「10行を超える関数はインライン化してはいけない」というものです。ただしデストラクタには注意が必要です。見た目よりも実際には長いことがよくあります。というのも、メンバクラスのデストラクタだけでなくベースクラスのデストラクタも暗黙に呼び出されるためです。

役に立つ経験則がもうひとつあります。それは「ループやスイッチ文のある関数をインライン化するのは割に合わない」というものです(ただし、よく見かける実行されないループやスイッチ文は除きます)。

インライン関数として宣言しても、実際にインライン化されるとは限らないことを知っておくことも重要です。たとえば仮想関数や再帰関数は通常、インライン化されません。再帰関数は通常、インライン化すべきではありません。仮想関数をインラインにするのは主に、仮想関数の定義をクラス内に置くのが便利であるから、あるいはアクセサやミューテータなどの動作を明記したいからです。

複雑なインライン関数を定義するときには、必要に応じてサフィックスが -inl.h であるファイルに書いても構いません。

インライン関数の定義はヘッダファイルに入れておく必要があります。そうすることでコンパイラはその定義を実際の呼び出し場所に埋め込めるようになります。しかしながら、実装コードはきちんと .cc ファイルに置くべきです。読みやすさやパフォーマンスの面でメリットがない限り、.h ファイルに大量のコードを入れるのは望ましくありません。

インライン関数の定義が短く、ロジックがほとんどないか、あっても少しであれば、コードを.h ファイルに入れるべきです。たとえばアクセサやミューテータは必ずクラス定義内に入れるべきです。たとえ複雑なインライン関数であっても、実装者や利用者にとって便利であれば .h ファイルに入れても構いません。ただし、.h ファイルが大きくなりすぎて扱いにくくなるなら、-inl.h という別のファイルにコードを入れても構いません。 こうするとクラス定義から実装が分離されてしまいますが、実装は必要な場所にきちんとインクルードされます。

-inl.h ファイルにはもうひとつ使い道があります。それは関数テンプレートを定義する場所としてです。テンプレートの定義を読みやすくするために、このファイルを利用しても構いません。

-inl.h ファイルにも #define ガードが必要であることを忘れないでください。これについては、他のヘッダファイルと全く同じです。

関数を定義するときには、パラメータの並びを「入力、出力」という順序にしてください。

C/C++関数のパラメータは、関数への入力、関数からの出力、その両方(入出力)のいずれかになります。入力パラメータは通常、値もしくは const リファレンスになります。これに対して、出力および入出力パラメータは、非 const ポインタになるでしょう。関数パラメータを並べるときには、まず入力オンリーなパラメータをすべて置き、それから出力パラメータを置いてください。新しいパラメータを追加するからといって、最後のパラメータとして追加してはいけません。入力オンリーなパラメータを新たに追加するときには、出力パラメータよりも前に置いてください。

これは厳格なルールではありません。というのも、入力であり出力でもあるパラメータ(クラスや構造体の場合が多い)もあって、ルールがうまく適用できないためです。また関連した関数群に一貫性を持たせるため、このルールを曲げなくてはならない場合もあります。

コードを読みやすくするため、そして依存関係が隠れてしまうのを避けるため、インクルードは、Cライブラリ、C++ライブラリ、その他ライブラリの .h、プロジェクトの .h、という順序にしてください。

プロジェクトのヘッダファイルはすべて、そのプロジェクトのソースディレクトリ配下にあるものとして書くべきです。UNIXにおけるディレクトリのショートカットである .(カレントディレクトリ)や ..(親ディレクトリ)を使ってはいけません。たとえば google-awesome-project/src/base/logging.h をインクルードするときには、以下のようにすべきです。

#include "base/logging.h"

dir/foo.ccdir/foo_test.cc の主目的が dir2/foo2.h にあるものを実装、テストすることなら、dir/foo.cc では次の順序でヘッダをインクルードしてください。

  1. dir2/foo2.h (適切なところ — 詳しくは下を参照)。
  2. Cシステムファイル
  3. C++システムファイル
  4. その他ライブラリの .h ファイル
  5. プロジェクトの .h ファイル

ここで推奨した順序にすると、dir/foo2.h が必要なインクルードを省略した場合、dir/foo.ccdir/foo_test.cc はビルドできなくなります。 このルールにしたがうことで、ほかのパッケージの無実の人ではなく、実際にこれらのファイルで作業している人に最初にビルドブレークを起こさせることができます。

dir/foo.ccdir2/foo2.h は同じディレクトリに置かれる場合が多いですが(たとえば base/basictypes_test.ccbase/basictypes.h)、別のディレクトリに置いても構いません。

各セクション内では、アルファベット順にインクルードするのが望ましいです。

たとえば google-awesome-project/src/foo/internal/fooserver.cc のインクルードは次のようになります。

#include "foo/public/fooserver.h" // 適切なところ #include <sys/types.h> #include <unistd.h> #include <hash_map> #include <vector> #include "base/basictypes.h" #include "base/commandlineflags.h" #include "foo/public/bar.h"
.cc ファイルには無名の名前空間を使うことを推奨します。もし名前付き名前空間を使うのであれば、プロジェクト名とできればパス名に基づいた名前にしてください。 using ディレクティブを使ってはいけません。 名前空間を使うことで、グローバルスコープを個別の名前の付いたスコープに分割できます。これはグローバルスコープにおける名前の衝突を回避するのに役立ちます。

名前空間はクラスによる(階層的な)名前の軸に加えて、(これもまた階層的な)もうひとつの名前の軸を提供します。

たとえば異なる2つのプロジェクトがあり、どちらのプロジェクトにもグローバルスコープに Foo というクラスがある場合、コンパイル時もしくは実行時にシンボルが衝突してしまうおそれがあります。それぞれのプロジェクトの名前空間にコードを置くと project1::Fooproject2::Foo といった異なるシンボルになり、衝突を回避できます。

名前空間を使うと混乱するおそれがあります。クラスによる名前の(階層的な)軸に加えて(階層的な)もうひとつの名前の軸ができるためです。

ヘッダファイルに無名のスコープを使うと、C++の単一定義規則(ODR、One Definition Rule)に違反しやすくなります。

名前空間を使うときには、以下のポリシーにしたがってください。 例にあるように、名前空間をコメントで閉じてください。

  • 実行時の名前の衝突を避けるために、.cc ファイルに無名の名前空間を使っても構いません。むしろ使うことを推奨します。 namespace { // .ccファイルにおいて // 名前空間のために余分なインデントはしない enum { kUnused, kEOF, kError }; // 共通で使うトークン bool AtEof() { return pos_ == kEOF; } // 名前空間内のEOFを使う } // namespace

    ただし特定のクラスに関連したファイルスコープ(トップレベル)を宣言する場合には、無名の名前空間のメンバではなく、そのクラスの型やスタティックデータメンバ、スタティックメンバ関数として宣言しても構いません。

  • .h ファイルには無名の名前空間を使ってはいけません。

名前付き名前空間は以下にしたがうべきです。

  • 名前空間は、インクルードと gflags 定義/宣言、そして他の名前空間にあるクラスの前方宣言以降のソースコードをすべてを含むようにします。 // .hファイルにおいて namespace mynamespace { // すべての宣言が名前空間のスコープ内にある。 // 余分なインデントを加えていないことに注意。 class MyClass { public: ... void Foo(); }; } // namespace mynamespace // .ccファイルにおいて namespace mynamespace { // 関数定義が名前空間のスコープ内にある。 void MyClass::Foo() { ... } } // namespace mynamespace

    .cc ファイルの場合は通常、もっと複雑になります。他の名前空間にあるクラスも参照する必要があるでしょう。

    #include "a.h" DEFINE_bool(someflag, false, "dummy flag"); class C; // グローバルな名前空間にあるクラスCの前方宣言 namespace a { class A; } // a::Aの前方宣言 namespace b { ...code for b... // コードの左側には余白を入れない。 } // namespace b
  • 名前空間 std には何も宣言してはいけません。たとえそれが標準ライブラリに含まれるクラスの前方宣言であってもです。名前空間 std に宣言したときの動作は未定義であり、移植性がないためです。標準ライブラリに含まれている要素を宣言するときには、適切なヘッダファイルをインクルードしてください。
  • using ディレクティブ を使って名前空間にある名前をすべて利用可能にしてはいけません。 // 以下は禁止 -- 名前空間が汚染されるため using namespace foo;
  • .cc ファイルのどこでも、.h ファイルの関数、メソッド、クラス内であれば、using 宣言 を使っても構いません。 // .ccファイルの場合にはどこでも使える。 // .hファイルの場合には関数、メソッド、クラス内に限り使える。 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ファイルにおけるエイリアスは、そのファイルをインクルードしている全員から見えることに注意してください。したがって、公開APIをできる限り小さくするためには、パブリックヘッダ(プロジェクト外部で利用可能なもの)とそこでインクルードしているヘッダにエイリアスを定義するのは避けるべきです。

ネストされたクラスがインタフェースの一部になっている場合には、パブリックなネストされたクラスを使っても構いません。ただしグローバルスコープを避けるために名前空間の使用を検討してください。 クラス内部に別のクラスを定義できます。これは メンバクラス とも呼ばれます。 class Foo { private: // Barはメンバクラスであり、Fooの内部にネストされている。 class Bar { ... }; }; そのクラス内だけで使うクラスには、ネストされたクラス(メンバクラス)を使うと便利です。クラスをメンバにすることで、外部のスコープを汚染することなく、そのクラス名をクラスのスコープ内に留めることができるためです。クラス内部でネストされたクラスを前方宣言して、.cc ファイルに実際のクラスを定義しても構いません。そうすればネストされたクラスの定義をその場に埋め込む必要はなくなります。ネストされたクラスの定義は通常、実装にのみ関係しているためです。 そのクラスの定義内でしか、ネストされたクラスを前方宣言できません。したがって Foo::Bar* ポインタを操作するヘッダファイルには、Foo のクラス宣言をすべて入れておく必要があるでしょう。 実際にインタフェースの一部、たとえば、あるメソッドのためのオプションを保持するクラスなどでない限り、ネストされたクラスをパブリックにしてはいけません。 グローバル関数を使うよりも、名前空間内の非メンバ関数やスタティックメンバ関数を使ってください。グローバル関数はめったに使ってはいけません。 非メンバ関数やスタティックメンバ関数を使った方がよい場合があります。非メンバ関数を名前空間内に置くことで、グローバルな名前空間の汚染を防げます。 外部リソースにアクセスしたり、大きな依存関係がある場合には、非メンバ関数やスタティックメンバ関数ではなく新しいクラスのメンバにした方がよい場合があります。

クラスのインスタンスに関連付けられていない関数を定義するのが役立つ場合もありますし、必要になることもあります。そうした場合には、関数をスタティックメンバ関数もしくは非メンバ関数にしても構いません。 非メンバ関数は外部変数に依存すべきではありません。そして、ほとんどの場合、名前空間内に置くべきです。スタティックデータを共有していないスタティックメンバ関数をひとつにまとめたい場合には、そのためのクラスを作るのではなく名前空間を使うべきです。

生成クラスと同じコンパイル単位に定義された関数が、別のコンパイル単位から直接呼び出された場合、不要な結合やリンク時の依存関係を作ってしまうおそれがあります。特にスタティックメンバ関数の場合によく起こります。この場合、新しいクラスとして抽出するか、可能であれば関数を別のライブラリの名前空間に置くよう検討してください。

もし非メンバ関数を定義する必要があり、それを必要としているのが .cc ファイルのみであるなら、無名の名前空間static リンク(たとえば static int Foo() {...})によってスコープを限定してください。

関数内で使う変数は、できるだけスコープを限定してください。そして宣言時には変数を初期化してください。

C++では関数内の任意の場所で変数を宣言できますが、私たちはできるだけスコープを局所化し、できるだけ最初に使う場所の近くで宣言することを推奨します。こうしておくと、その変数の型が何であり、どのように初期化されているのかが、コードを読む人にとってわかりやすくなります。具体的には「宣言や代入を使う代わりに初期化を使うべき」などです。

int i; i = f(); // わるい例 -- 宣言と初期化が分かれている。 int j = g(); // よい例 -- 宣言で初期化されている。

gcc では for (int i = 0; i < 10; ++i) が正しく実装されている(i のスコープは for ループ内のみ)ことに注意してください。そのため同じスコープにある別の for ループで再度 i が使えます。また ifwhile 文における宣言のスコープも正しく実装されています。

while (const char* p = strchr(str, '/')) str = p + 1;

ここでひとつ注意しておくべきことがあります。変数がオブジェクトの場合には、そのスコープに入るたびにコンストラクタが実行され、新たにオブジェクトが生成されます。そしてスコープから抜けるたびに、デストラクタが実行されます。

// 効率のわるい実装 for (int i = 0; i < 1000000; ++i) { Foo f; // コンストラクタとデストラクタがそれぞれ1000000回呼び出される。 f.DoSomething(i); }

したがってループ内で使う変数はループの外側で宣言した方が効率がよいでしょう。

Foo f; // コンストラクタとデストラクタはそれぞれ1回しか呼び出されない。 for (int i = 0; i < 1000000; ++i) { f.DoSomething(i); }
クラス型のスタティック変数やグローバル変数の使用を禁止します。これらは生成と解放の順序が不定であり、発見困難なバグにつながるためです。

グローバル変数、スタティック変数、スタティッククラスメンバ変数、関数スタティック変数などを含む、静的記憶域期間をもつオブジェクトは、POD(Plain Old Data)でなくてはなりません。PODとは、int、char、float、ポインタ、および、PODの配列/構造体のみでできていることを意味します。

C++ではコンストラクタとスタティック変数のイニシャライザの呼び出し順序が部分的にしか決められておらず、ビルドするたびに変わってしまう可能性があります。このことは発見困難なバグを引き起こすおそれがあります。したがってクラス型のグローバル変数を使うこと、そしてスタティックなPOD変数を関数の結果で初期化することを禁止します。ただし(getenv() や getpid() のように)関数が他のグローバル変数に依存している場合にはその限りではありません。

またデストラクタの呼び出し順序は、コンストラクタの呼び出し順序の逆順だと定義されています。コンストラクタの呼び出し順序が不定なので、デストラクタの呼び出し順序も不定になります。したがって、たとえばプログラム終了時にスタティック変数が破壊されたのに、(おそらく別のスレッドで)動いているコードがその変数にアクセスしようとして失敗する可能性があります。スタティックな「文字列」変数のデストラクタが、その文字列へのリファレンスを含む別の変数のデストラクタよりも先に呼び出されるおそれもあります。

結局のところ、スタティック変数はPODデータを含むときにしか使えないということです。vectorstring も許されません(代わりに、Cの配列や const char* を使ってください)。

もしクラス型のスタティック変数やグローバル変数が必要であれば、main() 関数か pthread_once() 関数で(決して free されない)ポインタを初期化することを検討してください。これはスマートポインタではなく、単なるポインタでなくてはならないことに注意してください。スマートポインタのデストラクタにも、これまで説明したのと同様、デストラクタの呼び出し順序の問題があるためです。

クラスはC++コードの基本単位であり、私たちも当然ながら至るところでクラスを使っています。このセクションでは、クラスを書くときに「すべきこと」「すべきではないこと」を挙げます。 一般的にコンストラクタでは、メンバ変数の初期値の設定だけをするべきです。複雑な初期化をする場合には、明示的に Init() メソッドを使うべきです。 コンストラクタ本体で初期化できます。 タイプするのに好都合です。クラスが初期化されているか気にする必要がなくなります。 コンストラクタにおける初期化には、いくつか問題があります。
  • 例外以外にコンストラクタがエラーになったことを知る簡単な方法がありません(そして例外を使うことは禁止されています)。
  • 処理に失敗すると、初期化の不完全なオブジェクトが取得できてしまいます。このオブジェクトは不定な状態になるおそれがあります。
  • 仮想関数を呼び出しても、サブクラス実装にディスパッチされません。 たとえ現時点ではサブクラス化していなくても、後になってサブクラス化したときに大きな問題になるおそれがあります。
  • だれかがクラス型のグローバル変数を作ると(これ自体ルールに違反していますが、それでも作ってしまった場合)、コンストラクタのコードが main() 関数よりも先に呼び出されてしまいます。これはコンストラクタのコードにとっておそらく想定外でしょう。 たとえば gflags はまだ初期化されていない状態になります。
オブジェクトの初期化が簡単なものでない限り、明示的な Init() メソッドを用意することを検討してください。特にコンストラクタでは、仮想関数を呼び出したり、エラーを発生しようとしたり、初期化されないおそれのあるグローバル変数にアクセスすべきではありません。
クラスにメンバ変数を定義していて他にコンストラクタがないときには、デフォルトコンストラクタを定義する必要があります。さもないとコンパイラが出来のわるいコンストラクタを定義してしまいます。 クラスオブジェクトを引数なしで new すると、デフォルトコンストラクタが呼び出されます。 new[] したときも同様に、デフォルトコンストラクタが呼び出されます(配列の場合)。 「取り得ない」値を保持するようデフォルトで初期化しておくと、デバッグがかなり楽になります。 コードを書く人にとって余計な作業になります。

クラスにメンバ変数を定義していて他にコンストラクタがないときには、デフォルトコンストラクタ(引数をとらないコンストラクタ)を定義しなければなりません。オブジェクトはできる限り矛盾がなく有効な内部状態になるよう初期化するべきです。

こうしたのは、他にコンストラクタがないのにデフォルトコンストラクタを定義していない場合、コンパイラがコンストラクタを生成してしまうためです。コンパイラが生成したコンストラクタはオブジェクトをうまく初期化できないおそれがあります。

もし既存のクラスから継承したクラスで、そこに新たなメンバ変数を追加していないのなら、デフォルトコンストラクタを用意する必要はありません。

引数が1つのコンストラクタにはC++の explicit キーワードを付けてください。 引数が1つのコンストラクタは通常、変換のために使われます。たとえば Foo::Foo(string name) が定義してあるとします。Foo が渡されることを期待している関数に文字列を渡すと、このコンストラクタが呼び出されて文字列を Foo に変換します。そして関数には Foo が渡されるのです。これは便利なのですが、この変換によって暗黙のうちに新しいオブジェクトが生成されることはトラブルの原因にもなり得ます。コンストラクタに explicit を宣言しておけば、暗黙のうちに変換されるのを防ぐことができます。 意図しない変換を避けられます。 ありません。

1つの引数をとるコンストラクタにはすべて、explicit を明示してください。クラス定義にある引数が1つのコンストラクタの前には、必ず explicit を付けてください。 explicit Foo(string name);

ただしコピーコンストラクタは例外です。こういうケースはめったにありませんが、explicit を付けるべきではないでしょう。 他のクラスに対する透過的なラッパーとして使うクラスも例外です。 こうした例外ケースの場合には、その旨コメントに明記しておくべきです。

コピーコンストラクタと代入演算子は、必要なときにのみ提供してください。必要がなければ、DISALLOW_COPY_AND_ASSIGN を使って無効にしてください。 コピーコンストラクタと代入演算子はオブジェクトのコピーを作るのに使われます。コピーコンストラクタはある状況(たとえばオブジェクトを値渡しするとき)において、コンパイラにより暗黙に呼び出されます。 コピーコンストラクタを使うとオブジェクトのコピーが簡単になります。STLコンテナに格納するコンテンツはすべて、コピー可能でかつ代入可能でなくてはなりません。コピーコンストラクタは CopyFrom() というスタイルよりも効率がよくなります。コピーコンストラクタはコピーの解釈も兼ねており、コンテキストに応じてコピーを省略し、ヒープ割り当てを避けることができるためです。 C++における暗黙のオブジェクトのコピーは、バグとパフォーマンス問題の温床になります。おまけにソースコードも読みにくくなります。どのオブジェクトがリファレンス渡しではなく値渡しなのか、それによってオブジェクトに対する変更がどこで反映されるのかが追跡困難になるためです。

ほとんどのクラスはコピー可能である必要はなく、コピーコンストラクタも代入演算子も持つべきではありません。たいていの場合、ポインタやリファレンスを使うことで、コピーした値と同様の振る舞いをパフォーマンスよく実現できます。たとえば関数パラメータには、値ではなくリファレンスやポインタを渡すことができます。またSTLコンテナには、オブジェクトではなくポインタを格納することができます。

もしクラスをコピー可能にする必要があるなら、コピーコンストラクタよりも CopyFrom()Clone() といったコピーメソッドを提供する方がよいです。こうしたコピーメソッドは暗黙に呼び出されることがないためです。コピーメソッドでは不十分な場合(たとえばパフォーマンスのためや、そのクラスをSTLコンテナの値として格納する必要がある場合)には、コピーコンストラクタと代入演算子の両方を提供してください。

もしクラスにコピーコンストラクタや代入演算子が必要ないなら、明示的に無効にしなくてはなりません。 これを実現するため、クラスの private: セクションにコピーコンストラクタと代入演算子のダミー宣言を追加してください。これらに対応する定義を提供してはいけません(こうしておくと、使おうとしたときにリンクエラーが発生します)。

以下のような DISALLOW_COPY_AND_ASSIGN マクロを使うと便利です。

// コピーコンストラクタと=演算子関数を無効にするマクロ // これはクラスのprivate:宣言内で使うべき。 #define DISALLOW_COPY_AND_ASSIGN(TypeName) \ TypeName(const TypeName&); \ void operator=(const TypeName&)

class Foo 内で以下のようにします。

class Foo { public: Foo(int f); ~Foo(); private: DISALLOW_COPY_AND_ASSIGN(Foo); };
struct はデータを運ぶ受動的なオブジェクトだけに使ってください。それ以外にはすべて class を使ってください。

C++の場合、キーワード structclass はどちらもほぼ同じように動作します。そこで私たちはこれらのキーワードに意味を持たせました。定義するデータの種類に合わせてキーワードを使い分けるべきです。

struct はデータを運ぶ受動的なオブジェクトに使うべきです。定数と関連付けても構いませんが、データメンバの取得/設定以外の機能を持たせてはいけません。フィールドの取得/設定には、メソッド呼び出しではなくフィールドに直接アクセスすることで実現します。メソッドは動作を提供するのではなく、データメンバの設定にのみ使われるべきです。つまり、コンストラクタ、デストラクタ、Initialize()Reset()Validate() といったメソッドのみにしてください。

これ以上の機能が必要な場合には、class を使う方が適切です。はっきりしなければ、class を使ってください。

STLと一貫性を持たせるため、functor や traint には class ではなく struct を使っても構いません。

構造体とクラスのメンバ変数には異なる命名規則があることに注意してください。

たいていの場合、継承よりもコンポジションを使った方が適切です。継承を使うときには、public 継承にしてください。 ベースクラスからサブクラスを継承すると、親のベースクラスに定義されているデータと操作の定義がすべて、そのサブクラスにも含まれることになります。実際にはC++の継承には大きく2つあります。ひとつは実装の継承で、実際のコードが子クラスによって継承されます。もうひとつはインタフェースの継承で、メソッド名だけが継承されます。 実装の継承というのは既存の型を特殊化するものです。ベースクラスにあるコードを再利用することで、コードサイズを減らすことができます。継承はコンパイル時に宣言されるので、あなたもコンパイラも何が行われるかがわかり、エラーを検出することができます。 これに対して、インタフェースの継承はクラスに特定のAPIを公開させるのに使えます。この場合もコンパイラは、クラスに必要なAPIメソッドが定義されていないときに、エラーを検出することができます。 実装の継承の場合、サブクラスの実装コードがベースクラスとサブクラスにまたがってしまうため、わかりにくくなります。またサブクラスでは非仮想関数を上書きできないため、サブクラスでその実装を変更できません。またベースクラスはデータメンバを定義しても構いませんが、こうするとベースクラスの物理的レイアウトを決めてしまいます。

継承はすべて public 継承にするべきです。プライベート継承を使いたければ、代わりにベースクラスのインスタンスをメンバとして持たせるべきです。

実装の継承を使いすぎてはいけません。実際のところ、コンポジションを使った方ががよい場合が多いです。継承を使うのは、"is-a" 関係があるときだけにしましょう。もし「BarFoo の一種(a kind of)である」と無理なく言えるなら、BarFoo のサブクラスにしても構いません。

必要に応じてデストラクタを virtual にしましょう。クラスに仮想メソッドがあるなら、そのクラスのデストラクタは virtual にすべきです。

protected を使うのは、サブクラスからアクセスする必要のあるメンバ関数だけに制限してください。データメンバはプライベートにすべきということに注意しましょう。

継承した仮想関数を再定義するときには、派生したクラスの宣言において virtual と明示的に宣言してください。virtual を省略してしまうと、その関数が仮想関数かどうか確認するには、そのクラスのすべての先祖をチェックしなくてはならないためです。

実際のところ、実装の多重継承が役に立つことはほとんどありません。ベースクラスのうち、実装を持つクラスが高々1つであり、それ以外のすべてのクラスが Interface というサフィックスのついた純粋なインタフェースクラスである場合に限り、多重継承を使っても構いません。 多重継承を使うとサブクラスが1つ以上のベースクラスを持てるようになります。ここでは純粋なインタフェースとしてのベースクラスと、実装を持つベースクラスとを区別しています。 実装の多重継承を使うことで、単一継承を使うよりもコードを再利用できる場合があります(継承を参照)。 実際のところ、実装の多重継承が役に立つことはまずありません。実装の多重継承を使うことで問題が解決すると思ったときには、もっと明確できれいな別の解決策が必ず見つかるはずです。 最初の1つを除くすべてのスーパークラスが純粋なインタフェースである場合に限り、多重継承を使うことを許します。純粋なインタフェースであることがわかるよう、これらのクラスには Interface というサフィックスを付けなくてはなりません。 Windows の場合には例外があります。 クラスがある条件を満たす場合には、そのクラスに Interface というサフィックスを付けることができます。ただし必須ではありません。

クラスが次の条件を満たす場合、それは純粋なインタフェースだと言えます。

  • パブリックな純粋仮想("= 0")メソッドとスタティックメソッドしかない(ただしデストラクタについては以下を参照)。
  • 非スタティックデータメンバが持てなくて構わない。
  • コンストラクタを定義する必要がない。コンストラクタがあるなら、引数なしで protected でなければならない。
  • サブクラスの場合には、これらの条件を満たすクラスから継承され、そのクラス名には Interface サフィックスが付いている

インタフェースクラスには純粋仮想メソッドが宣言されているため、直接インスタンス化することはできません。インタフェースの実装をすべて正しく確実に破棄するよう、インタフェースには仮想デストラクタも宣言しておく必要があります(最初のルールの例外として、これは純粋仮想メソッドにすべきではありません)。詳しくは、Stroustrup著 The C++ Programming Language 第3版のセクション 12.4 を参照してください。

クラス名に Interface というサフィックスを付けることで、実装を伴うメソッドや非スタティックデータメンバをそのクラスに追加してはいけないことがわかります。これは多重継承の場合に特に重要になります。 しかもインタフェースというコンセプトは、すでにJavaプログラマに広く理解されています。 Interface というサフィックスを付けるとクラス名が長くなり、読みにくく、理解しにくくなるおそれがあります。また、インタフェースという属性は、クライアントに公開すべきでない実装詳細のように思えます。 上の条件を満たす場合にのみ、クラス名に Interface を付けても構いません。ただし逆は要求されません。上の条件を満たすからといって、クラス名に Interface を付ける必要はありません。
特殊な状況を除き、演算子をオーバーロードしてはいけません。 組み込み型のように、クラスを操作する +/ といった演算子をクラスに定義できます。 クラスが(int などの)組み込み型と同じように使えるので、コードが直感的でわかりやすくなります。演算子のオーバーロードというのは、Equals()Add() といった面白味のない関数にしゃれた名前を付けるようなものです。正しく動かすために演算子を定義しなくてはならないテンプレート関数もあります。 演算子をオーバーロードするとコードは直感的にわかりやすくなりますが、次のような欠点もあります。
  • コストの高い操作なのに、コストの安い組み込み操作だと思わせてしまう。
  • オーバーロードした演算子を呼び出している場所を見つけるのが非常に困難になる。== の呼び出しを探すよりも Equals() を探す方がはるかに簡単です。
  • ポインタにも機能する演算子があるが、これらはバグを生みやすい。Foo + 4 がやることと、&Foo + 4 がやることは全く違います。コンパイラはどちらにも警告を出さないため、デバッグは非常に難しくなります。
オーバーロードには想定外の悪影響もあります。たとえばクラスが単項 operator& をオーバーロードしていると、クラスは安全に前方宣言できません。

一般的に、演算子のオーバーロードを使ってはいけません。特に代入演算子(operator=)はたちが悪いので避けるべきです。演算子のオーバーロードが必要なときには、Equals()CopyFrom() といった関数を定義できます。同様に、そのクラスが前方宣言される可能性があるなら、いかなる犠牲を払っても単項 operator& を避けてください。

しかし、演算子のオーバーロードが必要になる場合がまれにあります。それはテンプレートや "標準" C++クラス(ログのための operator<<(ostream&, const T&) など)とともに使うときです。十分妥当な理由がある場合には演算子のオーバーロードを使っても構いませんが、それでもできるだけ避けるべきです。具体的には、そのクラスがSTLコンテナのキーとして使われるからといって operator==operator< をオーバーロードしてはいけません。コンテナを宣言するときには、代わりに等価と比較の functor 型を作るべきです。

STLアルゴリズムには operator== のオーバーロードが必要なものがあります。その場合にはオーバーロードをしても構いませんが、その理由を明記しておいてください。

コピーコンストラクタ関数のオーバーロードも参照してください。

データメンバは private にして、必要に応じてアクセサ関数でアクセスできるようにしてください(技術的理由により、Google Test を使うときには test fixtureクラスのデータメンバを protected にしても構いません)。通常は変数を foo_ に、アクセサ関数を foo() という形式にします。ミューテータ関数は set_foo() という形式にしても構いません。 例外: static const データメンバ(通常は kFoo となる)は private である必要はありません。

アクセサの定義は通常、ヘッダファイルにインライン化してください。

継承関数名を参照してください。

クラスにおける宣言は、以下に定める順序にしてください。public:private: の前に置く、メソッドはデータメンバ(変数)の前に置くなどです。

クラスの定義は public: セクションから始めて、protected: セクション、private: セクションという順にすべきです。セクションが空の場合には省略します。

各セクション内では通常、次の順序で宣言するべきです。

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

フレンド宣言は必ず private セクションにあるべきです。また DISALLOW_COPY_AND_ASSIGN マクロは private: セクションの最後に入れるべきです。したがってこれがクラス定義の最後になります。コピーコンストラクタを参照してください。

.cc ファイルにおけるメソッドの定義は、できるだけ宣言の順序と同じにするべきです。

大きなメソッド定義をインライン化してクラス定義に入れてはいけません。インラインで定義してよいのは、簡単もしくはパフォーマンスに影響を及ぼすような、非常に簡潔なメソッドだけです。詳しくはインライン関数を参照してください。

関数は小さく、ポイントを絞るようにしましょう。

もちろん、長い関数の方が適切な場合もあります。したがって関数の長さには厳密な制限を設けません。もし関数が40行を超えるようであれば、プログラムの構造を変えずに関数を分割できないか検討しましょう。

たとえ長い関数が完璧に動いていても、数か月後にはだれかが変更して、新たな動作を追加するかもしれません。これは発見困難なバグにつながるおそれがあります。関数を短くシンプルにしておけば、他人が読みやすく、変更しやすいコードになります。

コードをながめていると、長くて複雑な関数が見つかることがあります。既存のコードを変更することを恐れてはいけません。扱いにくいと思ったり、デバックしにくいと感じたり、そのコードの一部を別のところでも使いたいと思ったら、関数をもっと小さく分割して、メンテナンスしやすくしましょう。

C++のコードをさらに堅牢なものにするため、私たちはいろいろなテクニックやユーティリティを使っています。ここでのやり方は、あなたがこれまで目にしてきたコードとは違っているかもしれません。

ポインタが本当に必要になったときには scoped_ptr を使いましょう。オブジェクトのオーナーシップを本当に共有する必要があるとき(たとえばSTLコンテナの内部)には、非constの std::tr1::shared_ptr を使うべきです。auto_ptr を使ってはいけません。 「スマート」ポインタはポインタのように機能しますが、基本的なメモリ管理を自動化してくれます。 スマートポインタはメモリリークを避けるのに極めて有用で、例外セーフなコードを書くのに不可欠なものです。また動的に割り当てられたメモリのオーナーシップを形式化し、文書化してくれます。 オブジェクトには単一かつ固定のオーナーがいるという設計にした方がよいです。スマートポインタを使うとオーナーシップの共有や移転が可能になるので、オーナーシップの意味を注意深く設計するよりも魅力的に思えるかもしれません。しかしこれには、コードを混乱させたり、メモリが削除されないといったバグを引き起こすおそれがあります。スマートポインタ(特に auto_ptr)の意味はわかりにくく、誤解を招くおそれがあります。例外セーフであるというスマートポインタの利点は決め手とはなりません。私たちは例外を許していないためです。
scoped_ptr
わかりやすく、特にリスクもありません。適切な場面で使いましょう。
auto_ptr
わかりにくく、オーナーシップの移転はバグになりがちです。使ってはいけません。
shared_ptr
constなリファレンスであれば安全です(つまり shared_ptr<const T>)。非constなリファレンスでリファレンスカウントされたポインタであればうまくいく場合もありますが、できる限り単一オーナーになるよう書き直しましょう。
cpplint.py を使ってスタイルのエラーを検出してください。

cpplint.py はソースファイルを読み込み、スタイルに関するいろいろなエラーを検出するツールです。ツールも完全ではなく、誤検出もあれば検出漏れもありますが、それでも十分役に立ちます。誤検出の場合には、その行末に // NOLINT を入れると無視してくれます。

プロジェクトで使っているツールから cpplint.py をどう実行するのか、きちんと説明されているプロジェクトもあります。あなたが参加しているプロジェクトにそういう説明がなくても、個別に cpplint.py をダウンロードして使えます。

リファレンス渡しのパラメータにはすべて、const を付けなければなりません。 Cの場合、関数で変数を変更するには、関数のパラメータにポインタを使う必要があります。たとえば int foo(int *pval) など。C++では別の方法として、リファレンスパラメータを宣言できます。int foo(int &val) パラメータをリファレンスとして定義すると、(*pval)++ といった見苦しいコードを避けられます。コピーコンストラクタなどにもリファレンスが必要になります。ポインタとは違い、リファレンスを使うと NULL といった値が取り得ないことを明確にできます。 リファレンスはまぎらわしいことがあります。構文は値のときと同じなのに、意味はポインタと同じになるためです。

関数パラメータのリストでは、すべてのリファレンスに const を付けなければなりません。

void Foo(const string &in, string *out);

実際のところ「入力パラメータは値もしくは const リファレンスとし、出力パラメータはポインタとする」というのが、Googleのコードにおける非常に厳格な規則となっています。入力パラメータに const ポインタを使っても構いませんが、非 const のリファレンスを使うことは許されません。

しかし入力パラメータの const T&const T* を使うのが適切な場合もあります。たとえば、 NULLで渡したい、 その関数は入力に対するポインタやリファレンスを保持する、といった場合です。 たいていの場合、入力パラメータは const T& になることを覚えておきましょう。const T* を使うときには、その入力は通常とは異なる扱いをすることをコードを読む人に伝えてください。const T& ではなく const T* を選ぶときには、具体的な理由を書いてください。そうしないとコードを読む人はありもしない理由を探して混乱してしまいます。

関数のオーバーロード(コンストラクタを含む)を使うのは、呼び出し側のコードを見ている人が、どのオーバーロードが呼び出されるのか正確に把握していなくても、何が起こっているかわかる場合だけにしてください。

const string& を引数とする関数を書き、それを const char* を引数とする別の関数でオーバーロードできます。

class MyClass { public: void Analyze(const string &text); void Analyze(const char *text, size_t textlen); };
オーバーロードを使うと引数の異なる関数に同じ名前を付けることができ、コードが直感的にわかりやすくなります。テンプレート化されたコードに必要とされる場合もあります。Visitorを使うときにも便利です。 関数が引数の型だけでオーバーロードされている場合、何が起こっているかコードを読む人が理解するためには、C++の複雑なマッチングルールを理解する必要があるでしょう。また派生クラスが関数のいくつかだけを上書きしていると、たいていの人は継承の意味に混乱してしまいます。 関数をオーバーロードしたくなったら、引数にちなんだ意味のある関数名になるよう検討してください。たとえば単なる Append() ではなく、AppendString()AppendInt() とするとよいでしょう。
関数のデフォルト引数を使ってはいけません。ただし、以下に説明するような特別な事情がある場合はその限りではありません。 たくさんデフォルト値を使う関数はよくありますが、時々そのデフォルトを上書きしたくなることがあります。デフォルト引数を使うと、まれな例外のために関数をたくさん定義する必要がなくなって簡単になります。 APIを実際に使っているコードを見ることで、そのAPIの使い方が理解できることはよくあります。 デフォルト引数をきちんと管理しておくのは困難です。過去のコードからコピー&ペーストしても、すべてのパラメータが見えているわけではないためです。コードの一部をコピー&ペーストしたとき、新規のコードにとってデフォルト引数が適切でない場合には大きな問題となります。

よく理解せずにそのままデフォルトをプログラマに受け入れさせるのでなく、そのAPIと引数として渡している値についてよく考えさせるよう、すべての引数を明示的に指定してください。ただし、以下の場合はその限りではありません。

可変長引数リストをシミュレートするためにデフォルト引数を使うのは例外とします。

// デフォルトの空のAlphaNumを使うことによって、パラメータを4つまでサポートする。 string StrCat(const AlphaNum &a, const AlphaNum &b = gEmptyAlphaNum, const AlphaNum &c = gEmptyAlphaNum, const AlphaNum &d = gEmptyAlphaNum);
可変長配列や alloca() を使ってはいけません。 可変長配列は自然な構文です。可変長配列と alloca() はどちらも非常に効率がよいものです。 可変長配列と alloca はC++標準の一部ではありません。さらに問題なことに、スタック空間にデータに依存したメモリを割り当てるため、発見困難なメモリの重ね書きバグを引き起こすおそれがあります。「ぼくのマシンだとうまく動くのに現場だとなぜか落ちてしまう」といった現象が発生します。 scoped_ptr/scoped_array などの安全なアロケータを使ってください。 妥当な範囲内であれば、friend クラスや friend 関数を使っても構いません。

フレンドは通常、同じファイルに定義すべきです。そうしておけば、コードを読む人が他のファイルの中身を見なくても、クラスのプライベートメンバが使われている場所を見つけることができます。friend がよく使われるのは、FooBuilder クラスを Foo のフレンドにするというものです。これにより、Foo の内部状態を全員に公開することなく、FooBuilder クラスは正しくオブジェクトを構築することができます。ユニットテストのクラスを、テスト対象となるクラスのフレンドにしておくと便利な場合もあります。

フレンドはクラスのカプセル化の範囲を拡げるものですが、カプセル化を破るものではありません。ある他のクラスだけからアクセスできるようにしたい場合には、メンバをパブリックにするよりも都合がよい場合もあります。しかし、たいていの場合、クラスはパブリックメンバを介して別のクラスとやり取りするべきです。

C++の例外を使ってはいけません。
  • 例外を使うことで、深く入れ子になった関数で発生した「起こり得ない」失敗をどう処理するか、アプリケーションの上位レベルで決定できます。曖昧で間違いの元となるエラーコード一覧といったものは不要になります。
  • 今や例外は数多くの言語で使われています。C++で例外を使えば、PythonやJava、それから全員がなじみのあるC++と一貫性を持たせることができます。
  • サードパーティ製の C++ ライブラリには例外を使っているものがあります。例外を内部的になくしてしまうと、こうしたライブラリとの統合が難しくなります。
  • 例外はコンストラクタが失敗したことを知る唯一の手段です。ファクトリ関数や Init() メソッドを使うことでシミュレーションも可能ですが、その場合にはヒープ割り当てや新たな「不正な」状態といったものが必要になります。
  • 例外はテストフレームワークにおいて本当に便利なものです。
  • 既存の関数に throw 文を追加するときには、途中にある呼び出し元をすべて調査しなければなりません。少なくとも、基本的な例外の安全性を保証するか、例外を捕捉せずに結果としてプログラムが中断してしまうことを良しとしなければなりません。たとえば f()g() を呼び出し、g()h() を呼び出しており、hf によって捕捉される例外を投げているなら、g には注意が必要があります。適切にクリーンナップされないおそれがあります。
  • しかも一般的に例外を使うと、コードを見ただけでは制御の流れを理解するのが難しくなります。関数は予期しない場所に戻ってくるかもしれません。これはメンテナンスやデバッグを困難にします。例外の使い方や使ってもよい場所についてルールを設けておけば、こうしたコストを最小限にできるでしょう。しかし開発者がそうしたルールを把握しておくには、さらにコストが必要になります。
  • 例外の安全性には、RAIIと異なるコーディングプラクティスの両方が必要になります。例外セーフなコードを正しく簡単に書くには、コンピュータによるさまざまな支援を必要とします。さらにコードを読む人がコールグラフ全体を理解しなくても済むようにするには、例外セーフなコードにおいて、不変状態を書き込むロジックを「コミット」フェーズに分離する必要があります。これはメリットにも、コストにもなります(コミットを分離すると、コードがわかりにくくならざるを得ません)。たとえ例外にそれほど価値がなくても、これは常にコストとなります。
  • 例外を有効にすると生成されるバイナリが大きくなり、コンパイル時間も長くなり(おそらくわずかですが)、アドレス空間を圧迫することになります。
  • 例外を使えるようにすると、開発者は適切でないときに例外を投げたり、安全でないときに例外から復帰しようとするおそれがあります。たとえば不正なユーザ入力があった場合には、例外を投げるべきではないでしょう。こうした制限を明文化しようとすると、スタイルガイドはさらに長くなってしまいます。

表面的には、例外を使うメリットはそのコストを上回ります。新規プロジェクトの場合にはなおさらです。しかし既存のコードに例外を導入しようとすると、依存関係のあるコードすべてに影響が及びます。もし例外が新規プロジェクトを超えて伝播する可能性があるなら、新規プロジェクトを例外を使っていない既存のコードに組み込むときに問題となります。Googleの既存のC++コードの多くは、例外を処理するように作られていません。したがって、例外を発生させるような新規コードを導入するのは、多少難しいことです。

Googleの既存コードに例外に対する耐性がないことを考慮すると、Googleのプロジェクトで例外を使うのは、新規プロジェクトで例外を使うのと比べて、ややコストが大きいと言えます。例外の変換プロセスには時間がかかり、問題になりやすいものです。また私たちはエラーコードやアサーションといった例外の代替手段が大きな障害になるとは考えていません。

例外を使うことに対する反対意見には、賢明あるいは高潔な理由があるわけではありません。これは現実的な理由からです。 Googleではオープンソースプロジェクトを積極的に利用したいと考えていますが、ここで説明した理由から、例外を使っているプロジェクトを採用するのは難しくなっています。したがって、Googleのオープンソースプロジェクトにおいても例外に対して反対意見を述べざるを得ません。 スクラッチから再度全部やり直せるなら、おそらく考えは違っていたでしょう。

Windowsのコードについては、このルールは例外です(シャレでなく)。

実行時型情報(RTTI)を使ってはいけません。 RTTIを使うと、プログラマは実行時にオブジェクトのC++クラスを問い合わせることができます。

RTTIはユニットテストに便利です。たとえばファクトリクラスをテストするとき、新しく生成されたオブジェクトが期待通りの型になっているか確認するのに使えます。

めったにないことですが、テスト以外にも役立つことがあります。

実行時に型を問い合わせるというのは、たいていの場合、設計に問題があるということです。もし実行時にオブジェクトの型を知る必要があるなら、クラス設計を再検討すべきだというサインかもしれません。

ユニットテスト以外でRTTIを使ってはいけません。オブジェクトのクラスによって異なる動作をするようなコードを書く必要があれば、別の方法で型を問い合わせることを検討しましょう。

サブクラスの型によって異なるコード実行パスをとりたいときには、仮想メソッドを使うとよいでしょう。それぞれの処理をそれぞれのオブジェクト内部に置くことができます。

現在処理しているコードではなくオブジェクトの外部に処理があるときには、Visitorデザインパターンのようなダブルディスパッチというテクニックの利用を検討しましょう。ダブルディスパッチを使うと、組み込みの型システムを使ってオブジェクトの外部でクラスの型を決定できます。

どうしてもこうしたアイデアが使えないのなら、RTTIを使っても構いません。ただし、そうする前にもう一度考えてください。:-) そして、もう一度考えてください。 RTTIに似たような回避策を自前で実装するのもいけません。型タグを持つクラス階層などの回避策にも、RTTIに対する議論がほとんどそのまま当てはまります。

static_cast<>() といった、C++のキャストを使ってください。int y = (int)x;int y = int(x); といった書式のキャストを使ってはいけません。 C++はCと異なるキャストの仕組みを導入しています。C++ではキャスト操作をいくつかの種類に分けています。 Cのキャストには操作が不明確だという問題があります。変換が行われることもあれば(たとえば (int)3.5)、キャストが行われることもあります(たとえば (int)"hello")。C++のキャストを使うと、こうした不明確さを避けられます。さらに、どこでキャストが使われているのか探すときにもわかりやすくなります。 構文が嫌らしいです。

Cスタイルのキャストを使ってはいけません。代わりにC++スタイルのキャストを使ってください。

  • 値の変換を行うようなCスタイルのキャストを使いたいとき、あるいは、あるクラスからそのスーパークラスに明示的にポインタをアップキャストする必要があるときには、static_cast を使ってください。
  • const 修飾子(constを参照)を取り除きたいときには、const_cast を使ってください。
  • 整数やその他のポインタ型との安全ではないポインタ型変換をしたいときときには、reinterpret_cast を使ってください。ただし使うのは、何をしているのかわかっていて、エイリアス問題についても理解しているときだけにしてください。
  • テストコード以外では dynamic_cast を使ってはいけません。 ユニットテスト以外で実行時に型情報を知る必要があるなら、おそらく設計上の欠陥があるのでしょう。
ストリームはログ記録のためだけに使ってください。 ストリームは printf()scanf() の代わりになります。 ストリームを使うと、プリントしているオブジェクトの型を知る必要がなくなります。書式文字列が引数リストとマッチしないといった問題は起こりません。(ただしgccだと printf でも問題は起こりません)。ストリームには自動のコンストラクタとデストラクタがあり、ファイルのオープンとクローズを自動的にやってくれます。 ストリームを使って pread() のような動作を実現するのは困難です。printf のようなハックを使わずにストリームを使って効率よくやるのは不可能ではありませんが、書式が複雑になるおそれがあります(具体的には、よく見かける文字列イディオム %.*s など)。ストリームは国際化に便利な演算子の並び換え(%1s ディレクティブ)をサポートしていません。

ログ記録のためのインタフェースとして必要な場合を除き、ストリームを使ってはいけません。代わりに printf などを使ってください。

ストリームの使用については賛否両論あります。いずれにせよ、この場合も一貫性を保つことが最優先です。コードにストリームを使ってはいけません。

この問題には長い議論があったので、理由についてさらに詳しく説明しておきます。たったひとつの指針を思い出してください。あるI/O型を使うときはいつでも、コードのどこであっても同じように見えるようにしたいのです。したがって、ストリームを使うのか read/write や printf を使うのか、ユーザが各自決められるようにしたくはありません。どちらか1つに決めるべきなのです。ただしログ記録については例外としました。これはかなり特殊な用途であり、歴史的な理由もあるためです。

ストリームの支持者らはストリームが選ばれるのは疑う余地がないと主張しましたが、実際のところ問題はそれほど明快ではありません。彼らが挙げるストリームの利点に対して、同じだけの欠点を挙げることができます。ストリームの最大の利点は、プリントするのにオブジェクトの型を知る必要がないことです。これには疑う余地はないでしょう。しかしここにはマイナス面もあります。間違った型を使ってしまいやすく、たとえ間違っていてもコンパイラは警告してくれません。ストリームを使うと、知らず知らずのうちに次のような間違いをしやすくなります。

cout << this; // アドレスを出力 cout << *this; // その内容を出力

このときコンパイラはエラーを吐いてくれません。<< がオーバーロードされているためです。これはオーバーロードを使うことを推奨していない理由でもあります。

printf は不恰好で読みにくいという人もいますが、ストリームにも同じことが言えるでしょう。次の2つのコードを見てみましょう。どちらにも同じ typo があります。どちらの方が見つけやすいですか?

cerr << "Error connecting to '" << foo->bar()->hostname.first << ":" << foo->bar()->hostname.second << ": " << strerror(errno); fprintf(stderr, "Error connecting to '%s:%u: %s", foo->bar()->hostname.first, foo->bar()->hostname.second, strerror(errno));

他にもいろいろと問題を挙げることができます。(あなたは「ラッパーを使えば改善できる」と主張するかもしれませんが、あるスキーマで正しいからといって他のスキーマでも正しいわけではありませんよね? それに私たちの目標は言語をもっと小さくすることであって、学習しなければならない仕組みをさらに増やすことではありません。)

どちらをとっても異なる利点と欠点があり、明らかに優れた解決策というものはありません。それでもシンプルさの原則にしたがって、どちらか1つに来めなくてはなりません。そして最終的に多数決により、printf + read/write が採用されたわけです。

イテレータやその他テンプレートオブジェクトの場合には、インクリメント演算子およびデクリメント演算子を前置形式(++i)で使ってください。 変数をインクリメントしたり(++ii++)デクリメントしたり(--ii--)し、その式の値を使わない場合、前置インクリメント(デクリメント)と後置インクリメント(デクリメント)のどちらを使うか選択する必要があります。 戻り値が無視される場合、「前置」形式(++i)を使うことで「後置」形式(i++)より効率が落ちることはありません。実際のところ効率がよくなることが多いです。後置インクリメント(またはデクリメント)の場合には、i のコピー(これが式の値になる)を作る必要があるためです。i がイテレータや非スカラ型の場合には、i をコピーするコストは高くついてしまいます。 値が無視されるときにはどちらも同じような動作をします。前置インクリメントだけにしない理由はありますか? Cでは式の値が使われない場合、特に for の場合、伝統的に後置インクリメントを使ってきました。後置インクリメントの方が読みやすいという人もいます。英語と同じように「主語」(i)が「動詞」(++)の前に来るためです。 単純なスカラ(非オブジェクト)値の場合にはどちらか一方を推奨する理由もなく、どちらを使っても構いません。イテレータやその他のテンプレート型の場合には、前置インクリメントを使ってください。 そうする意味があるときには必ず const を使うことを強く推奨します。 変数が変更されないことを示すために、変数やパラメータの宣言の前に const キーワードを付けることができます(たとえば const int foo)。また関数がクラスメンバ変数の状態を変更しないことを示すために、クラス関数に const という修飾子を付けることができます(たとえば class Foo { int Bar(char c) const; };)。 const を付けることで変数がどのように使われるか理解しやすくなります。コンパイラは型チェックができるため、よりよいコードを生成してくれます。また呼び出している関数が変数の変更を制限していることがわかり、プログラムの正しさを確かめるのにも役立ちます。マルチスレッドプログラムの場合、どの関数がロックなしで安全に使えるかを知るのにも役立ちます。 const は感染します。関数に const 変数を渡すと、その関数のプロトタイプには const を付けておく必要があります(さもなくば、変数を const_cast しなくてはなりません)。これは特にライブラリ関数を呼び出すときに問題になるおそれがあります。

変数、データメンバ、メソッド、引数に const を付けることで、コンパイル時の型チェックが高まります。これにより、できるだけすばやくエラーが検出できるようになります。 したがって、そうする意味があるときには必ず const を使うことを強く推奨します。

  • その関数がリファレンスやポインタで渡された引数を変更しないなら、引数に const を付けるべきです。
  • メソッドはできるだけ const だと宣言してください。アクセサはほぼ必ず const になるはずです。データメンバを変更することなく、非 constメソッドを呼び出すこともなく、データメンバに対する非 const ポインタや非 const リファレンスを戻すこともないなら、そうしたメソッドも const にするべきです。
  • 生成後に変更する必要のないデータメンバは必ず const にするよう検討してください。

ただし const を使おうとしすぎてもいけません。const int * const * const x; というコードはさすがにやりすぎです。たとえ x がどのように const であるか正確に記述していたとしてもです。 本当に意味のあるところに絞りましょう。この場合には const int** x で十分でしょう。

mutable キーワードを使っても構いませんが、これはマルチスレッド環境において安全ではありません。スレッドセーフかどうかを最初に注意深く検討するべきです。

const int* foo よりも int const *foo という書き方を好む人もいます。彼らは const は必ずそれが説明しているオブジェクトの次に来るというルールには一貫性があって読みやすくなると主張します。しかしこの場合には当てはまりません。「やりすぎてはいけない」という言葉により「一貫性がある」という主張がほぼ相殺されるためです。 最初に const を置いた方がほぼ間違いなく読みやすくなります。「名詞」(int)の前に「形容詞」(const)を置くと、英語と同じ語順になるためです。

const を最初に置くことを推奨しますが、必須ではありません。ただし、まわりのコードと一貫性を持たせてください。

組み込みのC++整数型のうち、使っていいのは int だけです。別のサイズの変数が必要であれば、<stdint.h> にある int16_t といった固定幅の型を使ってください。 C++では整数型のサイズを規定していません。通常、short は16ビット、int は32ビット、long は32ビット、long long は64ビットになります。 宣言に均一性が生まれます。 C++の整数型はコンパイラやアーキテクチャによって変わるかもしれません。

<stdint.h> には int16_tuint32_tint64_t といった型が定義されています。 shortunsigned long long を使う代わりに、必ずこれらの型を使うべきです。Cの整数型で使ってもいのは int だけにすべきです。必要があれば size_tptrdiff_t といった標準型を使っても構いません。

ループカウンタなどあまり大きくならないとわかっている整数には、int を使うことが非常に多いです。こうした場合には昔からある int をそのまま使いましょう。int は少なくとも32ビットあると考えても構いませんが、32ビット以上あると考えてはいけません。 もし64ビットの整数型が必要であれば、int64_tuint64_t を使ってください。

「大きくなる」ことがわかっている整数には、int64_t を使ってください。

表現しているのが実際には数値ではなくビットパターンであったり、2の補数オーバーフローを定義する必要があるときを除いて、uint32_t といった unsigned 整数型を使うべきではありません。特に数値が負にならないことを示したいだけのために unsigned 型を使ってはいけません。代わりにアサーションを使ってください。

教科書を書いている人のなかには、負にならない数値を表現するのに unsigned 型を使うことを推奨している人たちもいます。これは自己文書化のためです。しかしCの場合には、実際のところ文書化するメリットよりもバグを引き起こすデメリットの方が上回ります。次の例を考えてみましょう。

for (unsigned int i = foo.Length()-1; i >= 0; --i) ...

このコードは止まりません。gccはこの種のバグに気づいて警告してくれることもありますが、気づいてくれないことも多々あります。signed と unsigned を比較するときにも同じくらいひどいバグを引き起こすおそれがあります。つまり、Cの型昇格の仕組みによって、unsigned 型は期待と異なる動作をするおそれがあるのです。

したがって、変数が負でないことを示すにはアサーションを使ってください。unsigned 型を使ってはいけません。

コードは64ビットと32ビット、どちらでも動くようにするべきです。プリント文、比較、構造体のアラインメント問題を忘れないでください。
  • printf() の指定子には、32ビットシステムと64ビットシステムとの間で移植性がはっきりしないものがあります。C99には移植性のある書式指定子がいくつか定義されています。しかし残念ながら、MSVC 7.1はこうした指定子のいくつかを理解できず、標準とはなっていません。したがって自分で不恰好なバージョンを定義しなくてはならない場合もあります(標準のインクルードファイル inttypes.h のスタイルで)。

    // size_tのためのprintfマクロ。inttypes.hのスタイルで。 #ifdef _LP64 #define __PRIS_PREFIX "z" #else #define __PRIS_PREFIX #endif // これらのマクロを32ビットと64ビットどちらでも正しく動かすには、 // printf書式文字列において % の直後に用いること。 // 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では %zd

    PRI* マクロは、コンパイラにより連結された独立した文字列に展開されることに注意してください。したがって定数ではない書式文字列を使う場合には、その名前ではなくマクロの値を書式に入れておく必要があります。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ビット定数を用意する必要があるときには、LLULL というサフィックスを使ってください。たとえば次のようになります。 int64_t my_value = 0x123456789LL; uint64_t my_mask = 3ULL << 48;
  • 本当に32ビットシステムと64ビットシステムで異なるコードが必要になるなら、#ifdef _LP64 を使ってコードを分岐させてください。(できるだけこうするのを避け、変更を局所的にしましょう。)
マクロには十分注意してください。マクロを使うよりもインライン関数や enum、const 変数を使った方がよいです。

マクロを使うということは、あなたが見ているコードとコンパイラが見ているコードが違うということです。これは予期せぬ動作を引き起こすことがあります。特にマクロがグローバルスコープにあるときに問題になりやすいです。

幸運なことに、C++ではCほどマクロを必要としません。パフォーマンスに影響を及ぼすコードをインライン化するときには、マクロではなくインライン関数を使ってください。定数を格納するときには、マクロではなく const 変数を使ってください。長い変数名を省略するときには、マクロではなくリファレンスを使ってください。コードを条件付きコンパイルするときには、マクロではなく、いや、そもそもそんなことをしてはいけません(もちろんヘッダファイルの二重インクルードを防ぐための #define ガードは除きます)。そんなことをすると、テストが非常に難しくなります。

こうした他のやり方ではできない場合には、マクロを使っても構いません。コードベース、特に低レベルのライブラリではよく見かけるでしょう。また特殊な機能(文字列化、連結など)のいくつかは、言語によってはうまく使えません。しかしマクロを使う前には、マクロを使わずに同じ結果を達成する方法がないか注意深く検討してください。

以下の利用パターンにしたがうと、マクロを使ったときの問題の多くを回避できます。マクロを使うときにはできるだけ以下にしたがってください。

  • .h ファイルにマクロを定義してはいけません。
  • 使う直前に #define マクロを置き、使った直後に #undef してください。
  • 既存のマクロを自分が書いたものに置き換えたいからといって、既存のマクロを #undef してはいけません。その代わりに、ユニークな名前を持つマクロを使ってください。
  • アンバランスなC++構造体を拡張するためにマクロを使ってはいけません。少なくともその動作について明記してください。
  • 関数、クラス、変数を生成するのに ## を使うのは望ましくありません。
整数には 0 を、実数には 0.0 を、ポインタには NULL を、文字には '\0' を使ってください。

整数には 0 を使い、実数には 0.0 を使ってください。これには賛否両論あります。

ポインタ(アドレス値)には、0NULL という選択肢があります。Bjarne Stroustrup氏は簡素な 0 の方を好んでいます。私たちは NULL の方が好みです。こちらの方がポインタらしく見えるためです。実際のところC++コンパイラには gcc 4.1.0 のように、有用な警告をするために NULL に特別な定義付けをしているものもあります。具体的に言うと、sizeof(NULL)sizeof(0) と等しくない状況で警告してくれます。

文字には '\0' を使ってください。 これは型としても正しく、コードも読みやすくなります。

できる限り sizeof() ではなく sizeof(変数名) を使ってください。

sizeof(変数名) を使ってください。こうすると変数の型を変更してもうまく更新されるためです。 sizeof() の方がふさわしい場合もありますが、一般的には避けるべきです。変数の型を変更すると一致しなくなるおそれがあるためです。

Struct data; memset(&data, 0, sizeof(data)); memset(&data, 0, sizeof(Struct));

Boostライブラリは許可されているものだけを使ってください。 Boostライブラリは人気があり、十分にレビューされたフリーのオープンソースC++ライブラリ集です。 一般的にBoostのコードは非常に高品質で、移植性にすぐれ、C++標準ライブラリにある数多くの大きなギャップを type traints(型特性)やすぐれた binder やスマートポインタといったもので埋めてくれます。また標準ライブラリに対するTR1拡張の実装も提供してくれます。 Boostライブラリには、読みやすさを妨げるコーディングプラクティスを促すものもあります。メタプログラミングや先進的なテンプレートテクニック、行き過ぎた「関数」スタイルのプログラミングといったものです。

コードを読んでメンテナンスするコントリビュータ全員にとって読みやすくなるよう、私たちはBoostライブラリの一部のみを許可することにしました。現在のところ、以下のライブラリが許可されています。

  • Call Traitsboost/call_traits.hpp
  • Compressed Pairboost/compressed_pair.hpp
  • Pointer Containerboost/ptr_container)ただし、シリアライゼーションとC++03標準でないコンテナのためのラッパー(ptr_circular_buffer.hppptr_unordered*)を除く。
  • Arrayboost/array.hpp
  • Boost Graph Library(BGL)boost/graph)ただし、シリアライゼーション(adj_list_serialize.hpp )と並列/分散アルゴリズムとデータ構造(boost/graph/parallel/*boost/graph/distributed/*)を除く。
  • Property Mapboost/property_map)ただし、並列/分散プロパティマップ(boost/property_map/parallel/*)を除く。
  • Iterator の一部。以下のイテレータ定義に対応するもの。 boost/iterator/iterator_adaptor.hppboost/iterator/iterator_facade.hppboost/function_output_iterator.hpp

私たちはこれ以外のBoost機能のリスト追加についても引き続き検討しています。したがって、このルールは将来緩和されるかもしれません。

C++11(これまでC++0xとして知られていたもの)については許可されたライブラリと言語拡張のみを使ってください。現在のところ、許可されているものはありません。 C++11は最新のISO C++標準です。 これには言語とライブラリにとって重大な変更が含まれています。 C++11は公式標準になりました。やがてほとんどのC++コンパイラがサポートするでしょう。C++11はすでに利用されているC++拡張を標準化し、いくつかのオペレーションを省略可能にし、安全性を高めてくれます。

C++11標準はこれまで以上に複雑で(800ページが1,300ページに)、まだ開発者に広く知られてはいません。コードの読みやすさやメンテナンスに関して長期的に効果があるのか、まだ未知数です。こうした機能がいつ各種ツール(gcc、icc、clang、Eclipseなど)に実装されるのか、まだわかりません。

Boost 同様、C++11拡張には読みやすさの妨げになるコーディングプラクティスを促すものがあります。たとえばコードを読む人にとって役立つチェックの冗長性(型名など)の省略や、テンプレートメタプログラミングの助長といったものです。また既存の仕組みでも実現可能な機能と重複する拡張もあり、混乱と変換コストをもたらすおそれがあります。

許可されたC++11ライブラリおよび言語機能だけを使いましょう。現在のところ、許可されている機能はありません。必要に応じて個別に許可していく予定です。 C++11と互換性のないコードを書くのは避けましょう(たとえC++03では動いたとしてもです)。

一貫性を保つ上で最も重要なルールは命名に関するものです。命名スタイルがあると、名付けられたものが何なのか、型や変数、関数、定数、マクロなのか、などがすぐにわかります。宣言をあちこち探す必要もなくなります。頭の中のパターンマッチングエンジンは、命名規則を当てにしているのです。

命名規則にはかなり好みがありますが、私たちは個人の好みよりも一貫性を持つことの方が重要だと考えています。したがって、それが理にかなっていると感じても感じなくても、決まりは決まりだと考えてください。

関数名、変数名、ファイル名はわかりやすいものにすべきです。むやみに省略するのは避けてください。型や変数は名詞に、関数は「命令」的な動詞にするべきです。

無理のない範囲で、できるだけわかりやすい名前にしてください。スペースを節約しようとしてはいけません。新たにコードを読む人がすぐに理解できる方が、はるかに重要なことです。よい名前の例を以下に挙げます。

int num_errors; // よい例 int num_completed_connections; // よい例

よくない名前とは、曖昧な省略や意味のない、いい加減な言葉を使うことです。

int n; // わるい例 - 意味不明 int nerr; // わるい例 - 曖昧な略語 int n_comp_conns; // わるい例 - 曖昧な略語

型と変数の名前は通常、名詞にするべきです。たとえば FileOpenernum_errors など。

関数名は通常、命令的なものにすべきです(つまり「コマンド」にすべきです)。たとえば OpenFile()set_num_errors() など。アクセサはこの限りではありません。アクセスする変数と同じ名前にしてください。関数名のところで詳しく説明します。

プロジェクト外でも極めてよく知られている略語でない限り、使ってはいけません。例:

// よい例 // 省略がなく、適切な名前付け int num_dns_connections; // たいていの人は"DNS"が何を指しているか知っている。 int price_count_reader; // これもよい。priceのcountで意味はわかる。 // わるい例 // 以下のような略語は意味が曖昧で混乱をまねく。 int wgc_connections; // この意味がわかるのは一部の人だけ。 int pc_reader; // "pc"という略語はいろいろな意味で使われる。

文字を削って短縮してはいけません。

int error_count; // よい例 int error_cnt; // わるい例
ファイル名はすべて小文字にするべきです。アンダースコア(_)やダッシュ(-)を含んでも構いません。プロジェクトで採用している規則にしたがってください。したがうべき一貫性のあるパターンがなければ、"_" を使うとよいでしょう。

許されるファイル名の例。

my_useful_class.cc
my-useful-class.cc
myusefulclass.cc
myusefulclass_test.cc // _unittestと_regtestは廃止される予定。

C++ファイルは .cc で終わるべきです。ヘッダファイルは .h で終わるべきです。

db.h のように、/usr/include に存在するファイル名を使ってはいけません。

一般的に、ファイル名は具体的なものにしてください。たとえば logs.h ではなく http_server_logs.h としてください。たとえば FooBar というクラスを定義するときにはたいてい、foo_bar.hfoo_bar.cc という2つのファイルを使います。

インライン関数は .h ファイルに入れなければなりません。簡潔なインライン関数は .h ファイルに直接書くべきです。大きなインライン関数は -inl.h で終わる別のファイルに入れても構いません。インラインコードがたくさんあるクラスは3つのファイルで構成されることになります。

url_table.h // クラスの宣言 url_table.cc // クラスの定義 url_table-inl.h // たくさんのコードを含むインライン関数

-inl.hファイルのセクションも参照してください。

型名は大文字で始めて、単語の頭文字を大文字にして、アンダースコアを使わないようにしてください。MyExcitingClassMyExcitingEnum など。

すべての型、— クラス、構造体、typedef、enum — の名前には、同じ命名規則を使います。型名は大文字で始めて、単語の先頭を大文字にするべきです。アンダースコアは使いません。例:

// クラスと構造体 class UrlTable { ... class UrlTableTester { ... struct UrlTableProperties { ... // typedefs typedef hash_map<UrlTableProperties *, string> PropertiesMap; // enums enum UrlTableErrors { ...
変数名はすべて小文字にして、単語と単語の間にはアンダースコアを入れてください。クラスメンバ変数はアンダースコアで終わるようにします。たとえば my_exciting_local_variablemy_exciting_member_variable_ など。

例:

string table_name; // よい例 - アンダースコアを使っている。 string tablename; // よい例 - すべて小文字である。 string tableName; // わるい例 - 大文字小文字が混ざっている。

データメンバ(インスタンス変数やメンバ変数とも呼ばれる)は通常の変数名と同様、アンダースコアを含む小文字にします。ただし、必ずアンダースコアで終わってください。

string table_name_; // よい例 - アンダースコアで終わっている。 string tablename_; // よい例

構造体のデータメンバはクラスのデータメンバにある末尾のアンダースコアを除いて、通常の変数と同様に命名するべきです。

struct UrlTableProperties { string name; int num_entries; }

構造体とクラスの使い分けについては、構造体対クラスを参照してください。

グローバル変数に特別な決まりはありません。グローバル変数はめったに使うべきではありませんが、もし使うのであれば g_ などのプレフィックスを付けてローカル変数と区別しやすくするよう検討しましょう。

k で始まり、大文字小文字で続けてください。たとえば kDaysInAWeek など。

コンパイル時定数は、それがローカルであろうとグローバルであろうと、クラスの一部であろうとなかろうと、どのように宣言されていたとしても、他の変数とは全く異なる命名規則にしたがってください。k の後に単語の先頭を大文字にして続けます。

const int kDaysInAWeek = 7;
通常の関数は大文字小文字の組み合わせにしてください。アクセサやミューテータは変数名と一致させてください。 MyExcitingFunction(), MyExcitingMethod(), my_exciting_member_variable(), set_my_exciting_member_variable()

関数名は大文字で始めて、単語の先頭を大文字にするべきです。アンダースコアは使いません。

もし関数がエラー時にクラッシュするなら、関数名にOrDieを付加すべきです。これは製品コードで使われる可能性のある関数と通常のオペレーションで適度に発生するおそれのあるエラーにのみ適用されます。

AddTableEntry() DeleteUrl() OpenFileOrDie()

アクセサとミューテータ(get/set 関数)は取得および設定しようとする変数名と一致させるべきです。以下は num_entries_ というインスタンス変数を持つクラスの一部を示しています。

class MyClass { public: ... int num_entries() const { return num_entries_; } void set_num_entries(int num_entries) { num_entries_ = num_entries; } private: int num_entries_; };

非常に簡潔なインライン関数には小文字を使っても構いません。たとえばループ内でその関数を呼び出しても値をキャッシュしない非常に簡単なものであれば、小文字で命名しても構いません。

名前空間名はすべて小文字で、プロジェクト名とできればディレクトリ構造に基づく名前にしてください。たとえば google_awesome_project など。

名前空間とその命名については、名前空間を参照してください。

列挙子の名前は定数マクロと同様にすべきです。つまり kEnumNameENUM_NAME とします。

できるだけ列挙子は定数と同じようにすべきですが、マクロ と同じようにしても構いません。列挙型名 UrlTableErrors(および AlternateUrlTableErrors)は型なので、大文字と小文字を組み合わせた名前にしてください。

enum UrlTableErrors { kOK = 0, kErrorOutOfMemory, kErrorMalformedInput, }; enum AlternateUrlTableErrors { OK = 0, OUT_OF_MEMORY = 1, MALFORMED_INPUT = 2, };

2009年1月までは、enum値はマクロと同じように命名するスタイルでした。ところがこれにより、enum値とマクロの名前が衝突するという問題が発生しました。その結果、定数と同じスタイルで命名する方がよいと変更されました。新規のコードではできるだけ定数スタイルで命名すべきです。ただし、古い命名でも実際のコンパイル時に問題が発生していなければ、そのままで構いません。

マクロを定義しようとは思っていませんよね? もしマクロを定義するなら、次のようにしてください。 MY_MACRO_THAT_SCARES_SMALL_CHILDREN

マクロの説明を参照しましょう。一般的にマクロは使うべきではありません。どうしても必要なら、大文字とアンダースコアだけを使った名前にするべきです。

#define ROUND(x) ... #define PI_ROUNDED 3.0
すでにCやC++に存在しているものに似たものに名前を付ける場合には、既存の命名規則にしたがっても構いません。
bigopen()
関数名。open() の形にする。
uint
typedef
bigpos
structclasspos の形にしたがう。
sparse_hash_map
STLに似たエンティティ: STL の命名規則にしたがう。
LONGLONG_MAX
定数。INT_MAX と同様。

コメントを書くのは苦痛ですが、コードを読みやすくするのに不可欠です。以下のルールはどこにどんなコメントを書くべきか説明しています。 ただし忘れないでください。コメントはとても重要ですが、最高のコードとは自己文書化されたコードのことです。曖昧な名前を付けてコメントで説明しようとするより、型や変数に意味のある名前を付けておく方がはるかによいことなのです。

コメントはコードを読む人のために書いてください。つまりコードを理解する必要のある次のコントリビュータのためです。親切心をもってください — 次のコントリビュータはあなた自身かもしれません。

// もしくは /* */、どちらかを、一貫性を持って使ってください。

///* */、どちらの構文を使っても構いません。一般的には // の方がかなりよく見かけます。 どのようにコメントするか、どんなスタイルでどこにコメントするかには、一貫性を持たせてください。

ファイルはコピーライト宣言から始めてください。それからファイル内容に関する説明を書いてください。

すべてのファイルに以下の項目を以下の順序で入れるべきです。

  • コピーライトに関する文(たとえば Copyright 2008 Google Inc.
  • ライセンスの決まり文句。そのプロジェクトで使うライセンスに合わせて選択する。(たとえば Apache 2.0, BSD, LGPL, GPL)
  • ファイルの原作者がわかるような作者に関する行

もともと他人が書いたファイルに大きな変更をした場合には、作者のところに自分の名前を追加しましょう。これは他のコントリビュータがそのファイルについて質問をしたり、問い合わせ先を知る必要があるときに非常に役立ちます。

各ファイルの先頭(コピーライト宣言と作者に関する行の下)には、ファイルの内容に関するコメントを入れるべきです。

.h ファイルには通常、そのファイルで宣言しているクラスがどんなものでどのように使うのか、その概略を説明しておきます。.cc ファイルには、詳細実装やトリッキーなアルゴリズムの解説に関する詳細情報を入れておくべきです。.h を読む人にも実装詳細やアルゴリズム解説が役に立つと思うのであれば、それらを .h に入れても構いません。ただしその場合、.cc ファイルにはドキュメントが .h にある旨明記してください。

.h.cc との間でコメントをコピーしてはいけません。コピーするとコメントが別々に枝分かれしていくためです。

すべてのクラス定義には、そのクラスが何のためにあり、どのように使われるべきか説明するコメントを付けるべきです。 // Iterates over the contents of a GargantuanTable. Sample usage: // (GargantuanTableの中身をイテレートする。以下使用例。) // GargantuanTableIterator* iter = table->NewIterator(); // for (iter->Seek("foo"); !iter->done(); iter->Next()) { // process(iter->key(), iter->value()); // } // delete iter; class GargantuanTableIterator { ... };

クラスの詳細について、すでにファイルの先頭で説明しているなら、"See comment at top of file for a complete description"(「詳しくはファイル先頭のコメントを参照」)と書いても構いませんが、必ず何かしらコメントを入れるようにしてください。

クラスが想定する同期があれば、それについて書いておきましょう。クラスのインスタンスが複数のスレッドからアクセスされる可能性があるなら、マルチスレッドの利用に関するルールや不変条件について明記してあるか注意してください。

関数宣言のコメントにはその関数の使い方を書いてください。関数定義のコメントにはその処理内容について書いてください。

関数宣言にはすべて、その関数が何をするものであり、どうやって使うかを説明したコメントを、関数の直前に入れてください。このコメントは命令調(「ファイルをオープンしろ」)ではなく説明調(「ファイルをオープンする」)にすべきです。このコメントは関数について説明するものであって、関数が何をしているかを説明するものではありません。一般的に宣言のコメントというのは、関数がそのタスクをどうやっているかを説明するものではありません。そうしたコメントは関数宣言ではなく関数定義のコメントに入れるべきです。

関数宣言のコメントで言及すべきもの。

  • 入出力が何であるか。
  • クラスメンバ関数について、メソッドの呼び出し期間を超えてオブジェクトがリファレンス引数を覚えているのかどうか、それらを解放するのかしないのか。
  • その関数が呼び出し側で解放しなくてはならないメモリを割り当てているかどうか。
  • いずれかの引数が NULL になるおそれがあるかどうか。
  • 関数の使い方によってパフォーマンスに影響があるかどうか。
  • 関数がリエントラントかどうか。想定している同期は何か?

以下に例を挙げておきます。

// Returns an iterator for this table. It is the client's // responsibility to delete the iterator when it is done with it, // and it must not use the iterator once the GargantuanTable object // on which the iterator was created has been deleted. // // The iterator is initially positioned at the beginning of the table. // // This method is equivalent to: // Iterator* iter = table->NewIterator(); // iter->Seek(""); // return iter; // If you are going to immediately seek to another place in the // returned iterator, it will be faster to use NewIterator() // and avoid the extra seek. Iterator* GetIterator() const;

必要以上に冗長にしたり、完全に明確に記述しようとしてはいけません。以下のように「さもなければ false を返す」と書く必要はありません。これは書かなくてもわかります。

// Returns true if the table cannot hold any more entries. // (テーブルがこれ以上エントリを保持できないとき true を返す。) bool IsTableFull();

コンストラクタとデストラクタにコメントを入れるとき、コードを読む人は何のコンストラクタとデストラクタか知っていることを覚えておきましょう。「オブジェクトを破棄する」というコメントは意味がありません。そのコンストラクタが引数をどう処理するのか(たとえばポインタのオーナーシップがある場合)、そのデストラクタは何を解放するのか、といったことをコメントに入れておきましょう。ささいなことをコメントに入れる必要はありません。デストラクタにヘッダコメントがないことも多いです。

関数定義には、その関数がやっていることと、もしあればトリッキーな箇所を説明するコメントを入れるべきです。たとえば関数定義のコメントでは、使っているコーディングテクニックを説明したり、処理ステップの概要を説明したり、なぜ他ではなくその実装方法を選んだのかを説明しましょう。たとえば、なぜ関数の前半ではロックを取得する必要があり、なぜ後半ではその必要がないのか、といったことを説明するとよいでしょう。

.h や、どこかの関数宣言にあるコメントを繰り返すだけではいけないことに注意しましょう。その関数について簡単な要約を入れても構いませんが、コメントで重要なポイントは「どのようにしてそれをやるか」であるべきです。

一般的に変数名は説明的なものにし、その変数が何のために使われるかがわかるようにすべきです。さらにコメントを必要とする場合もあります。

クラスのデータメンバ(インスタンス変数やメンバ変数とも呼ばれる)には、何のために使われるか説明するコメントを入れるべきです。変数が NULL や -1 といった特別な意味のある値を番人として扱うときには、そのことを明記しておきましょう。例:

private: // Keeps track of the total number of entries in the table. // Used to ensure we do not go over the limit. -1 means // that we don't yet know how many entries the table has. // (テーブルの全エントリ数を記録する。限界を超えないようにするために使う。 // -1はテーブルのエントリ数がまだわからないことを意味する。) int num_total_entries_;

データメンバと同様に、グローバル変数にはすべて、それが何であり何のために使われるのか説明するコメントを入れるべきです。例:

// The total number of tests cases that we run through in this regression test. // (回帰テストで実行するテストケースの総数) const int kNumTestCases = 6;
実装では、トリッキーな箇所、不明瞭なところ、興味深いところ、重要なところにコメントを入れるべきです。

トリッキーな箇所や複雑なコードブロックがあれば、その直前にコメントを入れるべきです。例:

// Divide result by two, taking into account that x // contains the carry from the add. for (int i = 0; i < result->size(); i++) { x = (x << 8) + (*result)[i]; (*result)[i] = x >> 1; x &= 1; }

わかりにくい行には、行末にコメントを入れるべきです。行末のコメントは、コードからスペース2つ分空けるべきです。例:

// If we have enough memory, mmap the data portion too. // (メモリが十分あればデータ部分もmmapする。) mmap_budget = max<int64>(0, mmap_budget - index_->length()); if (mmap_budget >= data_size_ && !MmapData(mmap_chunk_bytes, mlock)) return; // Error already logged. (エラーはログに録済み。)

ここにはそのコードが何をしているか説明するコメントと、関数が戻るときにエラーがログに記録済みであることを述べたコメントがあることに注意しましょう。

コメントが複数行にわたるときには、開始位置をそろえると読みやすくなります。

DoSomething(); // Comment here so the comments line up. DoSomethingElseThatIsLonger(); // Comment here so there are two spaces between // the code and the comment. { // One space before comment when opening a new scope is allowed, // thus the comment lines up with the following comments and code. DoSomethingElse(); // Two spaces before line comments normally. }

関数に NULL やブーリアン、リテラル整数値を渡すときには、それが何であるかをコメントに書いたり、定数を使うことで自己文書化したコードにするよう検討するべきです。たとえば以下を比べてみましょう。

bool success = CalculateSomething(interesting_value, 10, false, NULL); // これら引数は何でしょう?

次のようにするとよいでしょう。

bool success = CalculateSomething(interesting_value, 10, // Default base value.(デフォルトのベース値。) false, // Not the first time we're calling this. // (これは初回の呼び出しではない。) NULL); // No callback.(コールバックなし。)

代わりに定数や自己文書化した変数を使っても構いません。

const int kDefaultBaseValue = 10; const bool kFirstTimeCalling = false; Callback *null_callback = NULL; bool success = CalculateSomething(interesting_value, kDefaultBaseValue, kFirstTimeCalling, null_callback);

コード自体を説明しようとしてはいけません。あなた以上にC++についてよく知っている人がコードを読んでいるのだと考えましょう。たとえ相手があなたのやろうとしていることを知らなくてもです。

// Now go through the b array and make sure that if i occurs, // the next element is i+1. ... // Geez. What a useless comment.
句読点、綴り、文法に注意しましょう。きちんと書けているコメントは読みやすいものです。

コメントは通常、ひとつの完結した文章として書くべきです。大文字を適切に使って、文末にはピリオドを付けてください。コード末尾のコメントなど、コメントが短くなればなるほど形式が失われがちです。しかしスタイルには一貫性を持たせるべきです。完結した文章の方が読みやすく、コメントが完結していれば未完成の思い付きではないことがある程度わかります。

コードレビュアーから「セミコロンを使うべきところでカンマを使っている」と指摘されるとイライラしますが、ソースコードの明確さ、読みやすさを高いレベルで保つことは極めて重要です。句読点や綴り、文法が適切であることは、それを実現する助けとなります。

TODO コメントは、一時的なものや短期的な解決策、その場しのぎの不完全な箇所に使ってください。

TODO にはすべて大文字の TODO という単語を使い、それからその TODO について一番説明できる人の名前やメールアドレスなどの識別子を入れるべきです。コロンはあってもなくても構いません。主たる目的は TODO の書式に一貫性を持たせて、もっと詳しく説明できる人がわかるようにすることです。TODO に書かれた人がその問題を修正することを約束するわけではありません。したがって、あなたが TODO を書くとき、ほとんどの場合は自分の名前を書くことになるでしょう。

// TODO(kl@gmail.com): Use a "*" here for concatenation operator. // TODO(Zeke) change this to use relations.

もし TODO が「将来の日付に何かする」という形式であれば、具体的な日付が入っているか(「2005年11月までに修正」)、もしくは具体的なイベントが入っているか(「すべてのクライアントがXMLレスポンスを処理できるようになったらこのコードを削除すること」)を確認しましょう。

推奨されないインタフェースには DEPRECATED というコメントを付けてください。

すべて大文字の DEPRECATED を含むコメントを書くことで、そのインタフェースがもはや推奨されないという印をつけることができます。このコメントはインタフェース宣言の前か同じ行に置きます。

DEPRECATED の後には、括弧で自分の名前やメールアドレスなどの識別子を書いておきましょう。

推奨されない旨のコメントには、呼び出し側を修正するためのシンプルで明確な指示が含まれていなくてはなりません。C++の場合、推奨されない関数を新しいインタフェースを呼び出すインライン関数として実装できます。

インタフェースに DEPRECATED という印をつけたからといって、魔法のように呼び出し側を変更してくれるわけではありません。推奨されないインタフェースを実際に使うのをやめさせたければ、呼び出し側を自分で修正するか、手伝ってくれる仲間を募る必要があるでしょう。

新規のコードには、推奨されないインタフェースの呼び出しを含むべきではありません。代わりに新しいインタフェースを使ってください。もし指示が理解できなければ、推奨されない旨を書いた人を見つけて、新しいインタフェースを使うの手伝ってもらうよう頼みましょう。

コーディングスタイルや書式にはかなり好みがありますが、全員が同じスタイルを使うとプロジェクトの見通しがよくなります。とはいえ書式のルールに合意してくれない人もいるでしょう。慣れるまで時間がかかるルールもあるでしょう。それでもプロジェクトのコントリビュータ全員が同じスタイルにしたがうことは重要です。これによって、全員のコードが読みやすく理解しやすくなるためです。

コードを正しい書式にするのに役立つよう、私たちは Emacs用設定ファイルを用意しています。

コードにおけるテキスト1行の長さは、長くても80文字にすべきです。

このルールには賛否両論ありますが、既存のコードのほとんどがこれを守っています。そして私たちは一貫性を持つことが重要だと思っています。

このルールに賛成している人は、コードを表示するためにウィンドウをリサイズさせるのは失礼だ、これ以上コードを長くする必要などないと言います。コーディングするときには複数のウィンドウを並べて使っており、これ以上ウィンドウを広げる余地などないという人もいます。みんな最大ウィンドウサイズを想定した作業環境を準備しています。そして80カラムが昔からの標準なのです。どうして今さら変える必要があるのでしょうか? 変更を支持している人は、1行を長くするとコードがもっと読みやすくなると言います。80カラムという制限はもはや時代遅れであって、1960年代のメインフレームの時代に後戻りするようなものです。最近の機器ではもっと画面が広く、もっと長い行を簡単に表示できます。

最長80文字とします。

例外: コメント行に80文字を超えるコマンド例やURL文字列が含まれる場合には、カット&ペーストが簡単できるよう80文字を超えても構いません。

例外: #include 文に80カラムを超える長いパスが含まれる場合。ただし、こんなものが必要にならないようにしましょう。

例外: #define ガードは最大長を超えても気にする必要はありません。

非ASCII文字はめったに使ってはいけません。使うときにはUTF-8フォーマットを使わなければなりません。

ユーザに表示するテキストをソースコードにハードコードしてはいけません。たとえ英語であってもです。したがって非ASCII文字が使われることはまずないはずです。ただし非ASCII文字をコードに入れると都合がよい場合もあります。たとえば外部からのデータファイルをパースするようなコードの場合、データファイルのデリミタとして非ASCII文字をハードコードしても構いません。(ローカライズの必要がない)ユニットテスト用のコードに非ASCII文字が含まれている場合もよくあります。この場合にもUTF-8を使うべきです。UTF-8であれば、単なるASCII文字以上を処理できるほとんどのツールで解釈できます。 Hexエンコーディングを使っても構いません。これを使うことで読みやすくなる場合もあります。たとえば "\xEF\xBB\xBF" はUnicodeのゼロ幅改行なし空白文字を意味していますが、ソースコードにUTF-8でこの通り書いても見えません。

スペースだけを使い、インデントはスペース2つにしてください。

インデントにはスペースを使います。コードにはタブを使ってはいけません。 タブキーを押したときにはスペースが入力されるようエディタを設定しておきましょう。

戻り値の型は関数名と同じ行に書いてください。1行に収まるのであれば、パラメータも同じ行に書きましょう。

関数は次のようになります。

ReturnType ClassName::FunctionName(Type par_name1, Type par_name2) { DoSomething(); ... }

長すぎて1行に収まらない場合には、次のようにします。

ReturnType ClassName::ReallyLongFunctionName(Type par_name1, Type par_name2, Type par_name3) { DoSomething(); ... }

最初のパラメータですら1行に収まらない場合には、次のようにします。

ReturnType LongClassName::ReallyReallyReallyLongFunctionName( Type par_name1, // スペース4つでインデント Type par_name2, Type par_name3) { DoSomething(); // スペース2つでインデント ... }

いくつか注意すべきポイントがあります。

  • 戻り値の型は必ず関数名と同じ行に書く。
  • 開き括弧('(')は必ず関数名と同じ行に書く。
  • 関数名と開き括弧の間にはスペースを入れてはいけない。
  • 括弧とパラメータの間にはスペースを入れてはいけない。
  • 開き中括弧('{')は必ず最後のパラメータと同じ行の末尾に書く。
  • 閉じ中括弧('}')はそのブロックの最後の行か、(もし別のスタイルルールで許されているなら)開き中括弧('}')と同じ行に書く。
  • 閉じ括弧(')')と開き中括弧('{')の間にはスペースを1つ入れるべき。
  • パラメータにはすべて、宣言や実装と同じ名前を付けるべき。
  • できる限りすべてのパラメータをそろえるべき。
  • デフォルトのインデントはスペース2つとする。
  • 次行に送ったパラメータはスペース4つでインデントする。

関数が const の場合、const キーワードは最後のパラメータと同じ行に書くべきです。

// 1行に収まる関数シグニチャ ReturnType FunctionName(Type par) const { ... } // 複数行を必要とする関数シグニチャ // constキーワードは最後のパラメータと同じ行に書く。 ReturnType ReallyLongFunctionName(Type par1, Type par2) const { ... }

未使用のパラメータがある場合には、関数定義で変数名をコメントアウトしてください。

// インタフェースのパラメータには必ず名前を付ける。 class Shape { public: virtual void Rotate(double radians) = 0; } // 宣言のパラメータには必ず名前を付ける。 class Circle : public Shape { public: virtual void Rotate(double radians); } // 定義されているが未使用のパラメータ名はコメントアウトしておく。 void Circle::Rotate(double /*radians*/) {} // わるい例。だれかが後で実装しようとしたとき、変数が何を意味しているかわからない。 void Circle::Rotate(double) {}
できるだけ1行に収めましょう。収まらなければ括弧で引数を改行しましょう。

関数呼び出しは次のような書式になります。

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

引数が1行に収まらない場合には、複数行に分割して、2行目以降を最初の引数にそろえるべきです。開き括弧の後や閉じ括弧の前にスペースを入れてはいけません。

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

関数に引数がたくさんある場合、コードが読みやすくなるのであれば、引数を1行ごとに書くことを検討しましょう。

bool retval = DoSomething(argument1, argument2, argument3, argument4);

関数シグニチャが長すぎて最大の行の長さに収まらないときには、すべての引数を2行目以降に書いても構いません。

if (...) { ... ... if (...) { DoSomethingThatRequiresALongFunctionName( very_long_argument1, // スペース4つでインデント argument2, argument3, argument4); }
括弧内にはスペースを入れないことを推奨します。else キーワードは新しい行に書きましょう。

基本的な条件文には2つの書式が許されています。ひとつは括弧と条件の間にスペースを入れるもので、もうひとつはスペースを入れないものです。

スペースを入れない書式の方がよく見かけると思います。どちらでも構いませんが一貫性を持たせてください。もし既存のファイルを変更しているなら、そこで使われている書式を使いましょう。もし新規のコードを書いているなら、そのディレクトリやプロジェクトで使われている書式を使いましょう。よくわからなくて個人的な好みもないのであれば、スペースを入れないでおきましょう。

if (condition) { // 括弧の中にはスペースをいれない。 ... // スペース2つでインデント } else if (...) { // else は閉じ中括弧と同じ行に続ける。 ... } else { ... }

お好みで括弧の中にスペースを入れても構いません。

if ( condition ) { // 括弧の中にスペースを入れている。あまり見ない。 ... // スペース2つでインデント } else { // else は閉じ中括弧と同じ行に続ける。 ... }

if と開き括弧の間には、必ずスペースを1つ入れる必要があることに注意しましょう。中括弧を使うときには、閉じ括弧と中括弧の間にもスペースを1つ入れなければなりません。

if(condition) // わるい例。IFの後にスペースがない。 if (condition){ // わるい例。{ の前にスペースがない。 if(condition){ // 二重にわるい例。 if (condition) { // よい例。IFの後にも{の前にも正しくスペースが入っている。

その方が読みやすいのであれば、短い条件文を1行にまとめて書いても構いません。ただしこれが許されるのは、その行が簡潔であり、else 節がないときだけです。

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

if文に else がある場合には許されません。

// これは許されない。ELSE節があるのにIF文を1行に書いている。 if (x) DoThis(); else DoThat();

一般に1行しかない文に中括弧を付ける必要はありませんが、中括弧を使うのが好みであれば付けても構いません。複雑な条件や文を含んだ条件文やループ文の場合には、中括弧を使った方が読みやすいかもしれません。if には必ず中括弧を付けなければならないとしているプロジェクトもあります。

if (condition) DoSomething(); // スペース2つでインデント if (condition) { DoSomething(); // スペース2つでインデント }

ただし if-else 文で中括弧を使っているところが1箇所でもあれば、他のところにも中括弧を使わなければなりません。

// これは許されない。IFには中括弧があるのにELSEには中括弧がない。 if (condition) { foo; } else bar; // これは許されない。ELSEには中括弧があるのにIFには中括弧がない。 if (condition) foo; else { bar; } // IF/ELSE節の1つに中括弧が使われており、IFとELSEどちらにも中括弧が必要になる。 if (condition) { foo; } else { bar; }
スイッチ文のブロックには中括弧を使っても構いません。空ループの本体には、{}continue を使うべきです。

switchcase ブロックには、中括弧を付けても付けけなくても構いません。中括弧を付けるのであれば以下のようにするべきです。

条件が列挙値でなければ、switch文には必ず default のケースを入れるべきです(列挙値の場合、値が処理されないおそれがあることをコンパイラが警告してくれます)。defaultのケースが起こり得ないときには assert を書いておきましょう。

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

空ループの本体には {}continue を使うべきです。セミコロンのみにしてはいけません。

while (condition) { // false が返ってくるまでテストを繰り返す。 } for (int i = 0; i < kSomeNumber; ++i) {} // よい例。空の本体。 while (condition) continue; // よい例。continueはロジックがないことを示している。 while (condition); // わるい例。do/while ループの一部のように見えてしまう。
ピリオドや矢印の前後にはスペースを入れてはいけません。ポインタ演算子の後ろにスペースを入れてはいけません。

ポインタとリファレンス表現の正しい書き方を以下に挙げます。

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

以下のことに注意してください。

  • メンバにアクセスするときにはピリオドや矢印のまわりにスペースを入れてはいけない。
  • ポインタ演算子では *& の後ろにスペースを入れてはいけない。

ポインタ変数やポインタ引数を宣言するときには、アスタリスクを型のそばに置いてもよいし、変数名のそばに置いても構いません。

// よい例。*、& の前にスペースがある。 char *c; const string &str; // よい例。*、 & の後にスペースがある。 char* c; // ただし "char* c, *d, *e, ...;" とするのを忘れてはいけない。 const string& str; char * c; // わるい例。* の前後にスペースがある。 const string & str; // わるい例。& の前後にスペースがある。

ファイル内では一貫性を持たせるべきです。既存のファイルを変更するときはそのファイルのスタイルにしたがいましょう。

標準の行の長さに収まらないブーリアン表現を書くときには、行の分割方法に一貫性を持たせてください。

以下では論理積演算子を必ず行末に置いています。

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

この例ではコードを折り返すとき、行末に && 論理積演算子を置いていることに注意しましょう。これはGoogleのコードでよく見かけますが、行頭に演算子を置くことも許されています。余分な括弧を入れても構いません。括弧をうまく入れると非常に読みやすくなります。また andcompl のようなワード演算子を使うのではなく、必ず &&~ のような句読点演算子を使うべきであることに注意してください。

return 式を必要以上に括弧で囲んではいけません。

x = expr; で括弧を使いたい場合に限り、return expr; で括弧を使ってください。

return result; // 単純な場合には括弧を使わない。 return (some_long_condition && // 複雑な式を読みやすくするため括弧を使うのは構わない。 another_condition); return (value); // var = (value); とは書かない。 return(result); // return は関数ではない。
初期化には = を使ってもよいし、() を使っても構いません。

初期化には = を使ってもよいし、() を使っても構いません。以下はすべて正しい書き方です。

int x = 3; int x(3); string name("Some Name"); string name = "Some Name";
プリプロセッサディレクティブのシャープは必ず行の先頭に置くべきです。

たとえインデントされたコード本体のなかでも、プリプロセッサディレクティブは行の先頭から書き始めるべきです。

// よい例。ディレクティブが行の先頭にある。 if (lopsided_score) { #if DISASTER_PENDING // よい例。行の先頭から書かれている。 DropEverything(); # if NOTIFY // #の後に空白を入れても構わないが、こうする必要はない。 NotifyClient(); # endif #endif BackToNormal(); } // わるい例。ディレクティブをインデントしている。 if (lopsided_score) { #if DISASTER_PENDING // "#if"は行の先頭にあるべき。 DropEverything(); #endif // "#endif"をインデントしてはいけない。 BackToNormal(); }
publicprotectedprivate の各セクションはこの順序で書いてください。これらのキーワードはスペース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_; DISALLOW_COPY_AND_ASSIGN(MyClass); };

以下のことに注意してください。

  • ベースクラス名はサブクラス名と同じ行に書くべき。ただし、長さは80カラムに制限される。
  • public:protected:private: といったキーワードは、スペース1つでインデントすべき。
  • 最初に書くキーワードを除いて、これらのキーワードの前には空行を入れるべき。ただし小さなクラスの場合にはオプションとします。
  • これらのキーワードの直後に空行を入れてはいけない。
  • public セクションが最初にきて、その後に protected、そして最後に private セクションがくるようにするべき。
  • セクション内の宣言の順序については、宣言の順序を参照。
コンストラクタイニシャライザリストはすべて1行に書くか、複数行で2行目以降をスペース4つでインデントして書きましょう。

イニシャライザリストには次の2つの書式が許されています。

// すべてが1行に収まるとき MyClass::MyClass(int var) : some_var_(var), some_other_var_(var + 1) {}

あるいは、

// 複数行にわたる場合、スペース4つでインデントして、イニシャライザの最初の行にコロンを入れる。 MyClass::MyClass(int var) : some_var_(var), // スペース4つでインデント some_other_var_(var + 1) { // 一列に並べる ... DoSomething(); ... }
名前空間内では余分なインデントをしてはいけません。

名前空間内では余分なインデントをしてはいけません。たとえば次のようにしてください。

namespace { void foo() { // よい例。名前空間内では余分なインデントをしてはいけない。 ... } } // namespace

名前空間内では余分なインデントをしてはいけません。

namespace { // わるい例。インデントすべきではない。 void foo() { ... } } // namespace

ネストされた名前空間を宣言するときには、1行につき名前空間1つにしてください。

namespace foo { namespace bar {
水平方向の空白を使ってよいかは場所によりけりです。行末に空白を入れてはいけません。 void f(bool b) { // 開き中括弧の前には、必ずスペースを1つ入れるべき。 ... int i = 0; // セミコロンの前には通常スペースを入れない。 int x[] = { 0 }; // 配列の初期化のための中括弧にはスペースを入れても入れなくても構わない。 int x[] = {0}; // もし入れるなら両側に入れる。 // 継承とイニシャライザリストのコロンのまわりにはスペースを1つ入れる。 class Foo : public Bar { public: // インライン関数の実装では、中括弧と実装本体との間にスペースを1つ入れる。 Foo(int b) : Bar(), baz_(b) {} // 空の中括弧の中にはスペースを入れない。 void Reset() { baz_ = 0; } // 実装と中括弧の間にスペースを1つ入れる。 ...

行末に空白が入っている場合、ほかの人が行末の空白を削除して同じファイルを編集すると、マージに余計な作業が発生するおそれがあります。したがって行末に空白を入れてはいけません。もし変更している行の末尾に空白があれば、その空白を削除してしまいましょう。あるいはクリーンナップ作業で削除してしまいましょう(できれば、だれもそのファイルで作業していないときに)。

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 ( ; i < 5 ; ++i) { // ループではセミコロンの後に必ずスペースを1つ入れる。 ... // セミコロンの前にもスペースを1つ入れても構わない。 switch (i) { case 1: // switch文におけるcaseのコロンの前にはスペースを入れない。 ... case 2: break; // コロンの後にコードを書くなら、スペースを1つ入れる。 x = 0; // 代入演算子のまわりには必ずスペースを入れる。 x = -5; // 単項演算子とその引数のまわりにはスペースを入れない。 ++x; if (x && !y) ... v = w * x + y / z; // 二項演算子のまわりには通常スペースを入れるが、 v = w*x + y/z; // 倍数のまわりのスペースは削除しても構わない。 v = w * (x + z); // 括弧内ではスペースを入れるべきでない。 vector<string> x; // 山括弧(< と >)の中、< の前、 y = static_cast<char*>(x); // キャストにおける > と ( の間にはスペースを入れない。 vector<char *> x; // 型とポインタの間にスペースを入れても構わないが、 // 一貫性を持たせること。 set<list<string> > x; // C++ では > と > の間にスペースが1つ必要になる。 set< list<string> > x; // < と < の間に対称となるようスペースを入れても構わない。
垂直方向の空白の使用は最小限にしてください。

これはルールというよりも原則です。不必要な空行を入れてはいけません。具体的に言うと、関数と関数の間に1行もしくは2行以上の空行を入れてはいけません。関数を空行で始めたくなるのをこらえましょう。関数を空行で終わらせてもいけません。関数内で空行を使うのは厳選しましょう。

基本原則: コードが1画面にうまく収まっていると、プログラムの制御フローが追いやすく理解しやすくなります。言うまでもないことですが、コードが密集しすぎても拡散しすぎても、読みやすくはなりません。自分自身で判断しましょう。一般的には垂直方向の空白の使用は最小限にしてください。

どんなときに空行を入れるとよいのか、次のような経験則があります。

  • 関数の最初や最後に空行を入れても、読みやすくなることはまずない。
  • 一連の if-else ブロックに空行を入れると、読みやすくなることがある。

ここで説明したコーディング規則は必須です。しかし、どんなに優れたルールにも例外はつきものです。ここではそうした例外について説明します。

このスタイルガイドに準拠していないコードを扱うときには、これらのルールを逸脱しても構いません。

このスタイルガイドにあるルールではなく、別のスタイルガイドにしたがって書かれたコードに手を加えることもあるでしょう。そうした場合には、ローカルなコード規則と一貫性を持たせるために、このガイドにあるルールから逸脱しなくてはならないかもしれません。疑問を感じたときには、コードの原作者や現在の責任者に尋ねましょう。一貫性にはローカルな一貫性も含まれていることを忘れてはいけません。

Windowsプログラマは自らのコーディング規則を作り上げてきました。それらは主にWindowsのヘッダファイルやその他Microsoftのコードに由来しています。これに対して、私たちはコードをだれにでも理解しやすくしたいと考えています。そこでC++でプログラムを書く人たちのために、どんなプラットフォームでも使えるガイドラインを作りました。

すでに広く蔓延しているWindowsスタイルを使っていたなら、このガイドラインに書かれたことを忘れているかもしれません。もう一度言っておきます。

  • ハンガリアン記法を使ってはいけません(たとえば整数に iNum という名前を付けるなど)。Googleの命名規則にしたがい、ソースファイルには .cc という拡張子を付けてください。
  • Windowsではプリミティブ型にさまざまな別名を定義しています。たとえば DWORDHANDLE など。Windows API関数を呼び出すときにはこれらの型を使っても構いませんし、むしろ使うことを推奨します。ただし、できる限り基本となるC++型を使ってください。たとえば LPCTSTR ではなく const TCHAR * を使いましょう。
  • Microsoft Visual C++でコンパイルするときには警告レベルを3以上に設定して、すべての警告をエラーとして扱ってください。
  • #pragma once を使ってはいけません。代わりに標準的なGoogleのインクルードガード(#define ガード)を使いましょう。インクルードガードに使うパスはプロジェクトツリーのトップに対する相対パスにすべきです。
  • 実際のところ、絶対に必要なとき以外は #pragma__declspec といった非標準の拡張を使ってはいけません。__declspec(dllimport)__declspec(dllexport) は使っても構いませんが、DLLIMPORTDLLEXPORT といったマクロ経由で使いましょう。そうすればあとで簡単に無効化できます。

そうは言っても、Windowsの場合には時として違反せざるを得ないルールもあります。

  • 私たちは実装の多重継承の使用を禁止していますが、COMやATL/WTLクラスを使うときにはこれが必要になります。COMやATL/WTLクラスやインタフェースを実装するためであれば、実装の多重継承を使っても構いません。
  • 自分で書くコードには例外を使うべきではありませんが、ATLやSTL、Visual C++に関連したコードではいろいろなところに例外が使われています。ATLを使うときには、例外を無効にするため _ATL_NO_EXCEPTIONS を定義すべきです。STLを使うときにも、例外を無効にできないか調べるべきです。もしそれができなくても、コンパイラで例外を無効にできます。(これはSTLをコンパイルする場合に限ることに注意しましょう。例外を処理するコードを自分で書くべきではありません。)
  • プリコンパイルヘッダを使うときには、ソースファイルの先頭でヘッダファイルをインクルードしましょう。ヘッダファイルは通常、StdAfx.hprecompile.h という名前になっています。ほかのプロジェクトとコードを共有しやすくするために、明示的にファイルをインクルードするのではなく(precompile.ccの中を除く) /FI コンパイルオプションでファイルを自動的にインクルードしましょう。
  • リソースヘッダは resource.h という名前にして、マクロだけを含むようにしましょう。その内容については本スタイルガイドに準拠する必要はありません。

常識を働かせましょう、そして一貫性を持たせましょう。

コードを書いているところであれば、数分かけてまわりのコードを調べてスタイルを決めましょう。もし if 節のまわりに空白があれば、あなたもそうすべきです。もしコメントがスターで囲まれた小さなボックス状になっていれば、あなたのコメントもそうしましょう。

スタイルにガイドラインがあるということは、コーディングに共通のボキャブラリがあるということです。ガイドラインがあるおかげで、どう話すのかでなく、何を話すのかに集中できるようになります。さて、ここでは主にスタイルに関連したルールについて説明してきました。もう十分ボキャブラリについてはわかったはずです。ただし、ローカルなスタイルも重要だということを忘れないでください。あなたの書いたコードがまわりにある既存のコードと全然違って見えてしまうと、コードを読む人はバラバラに感じて、リズムが狂ってしまいます。そうなるのは避けましょう。

さて、コードの書き方についてはもう十分でしょう。コードを書く方がずっと楽しいのですから。さあ、楽しみましょう!


Revision 3.199

Benjy Weinberger
Craig Silverstein
Gregory Eitzmann
Mark Mentovai
Tashana Landray
(日本語訳 Takashi Sasai