Revision 3.199
Benjy Weinbergerやったね! このように矢印ボタンをクリックすると詳細情報が表示されます。 このドキュメントの先頭にある大きな矢印ボタンをクリックしても構いません。そうすれば、すべての詳細情報が表示されます。
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
ガードにします。
#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.cc
で Foo
を使っていて、インクルードしている myfile.h
で Foo
をインクルード(あるいは前方宣言)している場合は、その限りではありません。
インライン化すべきか否かについては、優れた経験則があります。それは「10行を超える関数はインライン化してはいけない」というものです。ただしデストラクタには注意が必要です。見た目よりも実際には長いことがよくあります。というのも、メンバクラスのデストラクタだけでなくベースクラスのデストラクタも暗黙に呼び出されるためです。
役に立つ経験則がもうひとつあります。それは「ループやスイッチ文のある関数をインライン化するのは割に合わない」というものです(ただし、よく見かける実行されないループやスイッチ文は除きます)。
インライン関数として宣言しても、実際にインライン化されるとは限らないことを知っておくことも重要です。たとえば仮想関数や再帰関数は通常、インライン化されません。再帰関数は通常、インライン化すべきではありません。仮想関数をインラインにするのは主に、仮想関数の定義をクラス内に置くのが便利であるから、あるいはアクセサやミューテータなどの動作を明記したいからです。
-inl.h
であるファイルに書いても構いません。
インライン関数の定義はヘッダファイルに入れておく必要があります。そうすることでコンパイラはその定義を実際の呼び出し場所に埋め込めるようになります。しかしながら、実装コードはきちんと .cc
ファイルに置くべきです。読みやすさやパフォーマンスの面でメリットがない限り、.h
ファイルに大量のコードを入れるのは望ましくありません。
インライン関数の定義が短く、ロジックがほとんどないか、あっても少しであれば、コードを.h
ファイルに入れるべきです。たとえばアクセサやミューテータは必ずクラス定義内に入れるべきです。たとえ複雑なインライン関数であっても、実装者や利用者にとって便利であれば .h
ファイルに入れても構いません。ただし、.h
ファイルが大きくなりすぎて扱いにくくなるなら、-inl.h
という別のファイルにコードを入れても構いません。
こうするとクラス定義から実装が分離されてしまいますが、実装は必要な場所にきちんとインクルードされます。
-inl.h
ファイルにはもうひとつ使い道があります。それは関数テンプレートを定義する場所としてです。テンプレートの定義を読みやすくするために、このファイルを利用しても構いません。
-inl.h
ファイルにも #define
ガードが必要であることを忘れないでください。これについては、他のヘッダファイルと全く同じです。
C/C++関数のパラメータは、関数への入力、関数からの出力、その両方(入出力)のいずれかになります。入力パラメータは通常、値もしくは const
リファレンスになります。これに対して、出力および入出力パラメータは、非 const
ポインタになるでしょう。関数パラメータを並べるときには、まず入力オンリーなパラメータをすべて置き、それから出力パラメータを置いてください。新しいパラメータを追加するからといって、最後のパラメータとして追加してはいけません。入力オンリーなパラメータを新たに追加するときには、出力パラメータよりも前に置いてください。
これは厳格なルールではありません。というのも、入力であり出力でもあるパラメータ(クラスや構造体の場合が多い)もあって、ルールがうまく適用できないためです。また関連した関数群に一貫性を持たせるため、このルールを曲げなくてはならない場合もあります。
.h
、プロジェクトの .h
、という順序にしてください。
プロジェクトのヘッダファイルはすべて、そのプロジェクトのソースディレクトリ配下にあるものとして書くべきです。UNIXにおけるディレクトリのショートカットである .
(カレントディレクトリ)や ..
(親ディレクトリ)を使ってはいけません。たとえば google-awesome-project/src/base/logging.h
をインクルードするときには、以下のようにすべきです。
dir/foo.cc
や dir/foo_test.cc
の主目的が
dir2/foo2.h
にあるものを実装、テストすることなら、dir/foo.cc
では次の順序でヘッダをインクルードしてください。
dir2/foo2.h
(適切なところ
— 詳しくは下を参照)。.h
ファイル.h
ファイル
ここで推奨した順序にすると、dir/foo2.h
が必要なインクルードを省略した場合、dir/foo.cc
や dir/foo_test.cc
はビルドできなくなります。
このルールにしたがうことで、ほかのパッケージの無実の人ではなく、実際にこれらのファイルで作業している人に最初にビルドブレークを起こさせることができます。
dir/foo.cc
と dir2/foo2.h
は同じディレクトリに置かれる場合が多いですが(たとえば base/basictypes_test.cc
と base/basictypes.h
)、別のディレクトリに置いても構いません。
各セクション内では、アルファベット順にインクルードするのが望ましいです。
たとえば google-awesome-project/src/foo/internal/fooserver.cc
のインクルードは次のようになります。
.cc
ファイルには無名の名前空間を使うことを推奨します。もし名前付き名前空間を使うのであれば、プロジェクト名とできればパス名に基づいた名前にしてください。
名前空間はクラスによる(階層的な)名前の軸に加えて、(これもまた階層的な)もうひとつの名前の軸を提供します。
たとえば異なる2つのプロジェクトがあり、どちらのプロジェクトにもグローバルスコープに Foo
というクラスがある場合、コンパイル時もしくは実行時にシンボルが衝突してしまうおそれがあります。それぞれのプロジェクトの名前空間にコードを置くと project1::Foo
、project2::Foo
といった異なるシンボルになり、衝突を回避できます。
名前空間を使うと混乱するおそれがあります。クラスによる名前の(階層的な)軸に加えて(階層的な)もうひとつの名前の軸ができるためです。
ヘッダファイルに無名のスコープを使うと、C++の単一定義規則(ODR、One Definition Rule)に違反しやすくなります。
名前空間を使うときには、以下のポリシーにしたがってください。 例にあるように、名前空間をコメントで閉じてください。
.cc
ファイルに無名の名前空間を使っても構いません。むしろ使うことを推奨します。
ただし特定のクラスに関連したファイルスコープ(トップレベル)を宣言する場合には、無名の名前空間のメンバではなく、そのクラスの型やスタティックデータメンバ、スタティックメンバ関数として宣言しても構いません。
.h
ファイルには無名の名前空間を使ってはいけません。
名前付き名前空間は以下にしたがうべきです。
.cc
ファイルの場合は通常、もっと複雑になります。他の名前空間にあるクラスも参照する必要があるでしょう。
std
には何も宣言してはいけません。たとえそれが標準ライブラリに含まれるクラスの前方宣言であってもです。名前空間 std
に宣言したときの動作は未定義であり、移植性がないためです。標準ライブラリに含まれている要素を宣言するときには、適切なヘッダファイルをインクルードしてください。
.cc
ファイルのどこでも、.h
ファイルの関数、メソッド、クラス内であれば、.cc
ファイルのどこでも、.h
ファイル全体を包む名前付き名前空間内のどこでも、関数やメソッド内であれば、名前空間のエイリアスを使っても構いません。
.hファイルにおけるエイリアスは、そのファイルをインクルードしている全員から見えることに注意してください。したがって、公開APIをできる限り小さくするためには、パブリックヘッダ(プロジェクト外部で利用可能なもの)とそこでインクルードしているヘッダにエイリアスを定義するのは避けるべきです。
.cc
ファイルに実際のクラスを定義しても構いません。そうすればネストされたクラスの定義をその場に埋め込む必要はなくなります。ネストされたクラスの定義は通常、実装にのみ関係しているためです。
Foo::Bar*
ポインタを操作するヘッダファイルには、Foo
のクラス宣言をすべて入れておく必要があるでしょう。
クラスのインスタンスに関連付けられていない関数を定義するのが役立つ場合もありますし、必要になることもあります。そうした場合には、関数をスタティックメンバ関数もしくは非メンバ関数にしても構いません。 非メンバ関数は外部変数に依存すべきではありません。そして、ほとんどの場合、名前空間内に置くべきです。スタティックデータを共有していないスタティックメンバ関数をひとつにまとめたい場合には、そのためのクラスを作るのではなく名前空間を使うべきです。
生成クラスと同じコンパイル単位に定義された関数が、別のコンパイル単位から直接呼び出された場合、不要な結合やリンク時の依存関係を作ってしまうおそれがあります。特にスタティックメンバ関数の場合によく起こります。この場合、新しいクラスとして抽出するか、可能であれば関数を別のライブラリの名前空間に置くよう検討してください。
もし非メンバ関数を定義する必要があり、それを必要としているのが .cc
ファイルのみであるなら、無名の名前空間か static
リンク(たとえば static int Foo() {...}
)によってスコープを限定してください。
C++では関数内の任意の場所で変数を宣言できますが、私たちはできるだけスコープを局所化し、できるだけ最初に使う場所の近くで宣言することを推奨します。こうしておくと、その変数の型が何であり、どのように初期化されているのかが、コードを読む人にとってわかりやすくなります。具体的には「宣言や代入を使う代わりに初期化を使うべき」などです。
gcc では for (int i = 0; i < 10; ++i)
が正しく実装されている(i
のスコープは for
ループ内のみ)ことに注意してください。そのため同じスコープにある別の for
ループで再度 i
が使えます。また if
や while
文における宣言のスコープも正しく実装されています。
ここでひとつ注意しておくべきことがあります。変数がオブジェクトの場合には、そのスコープに入るたびにコンストラクタが実行され、新たにオブジェクトが生成されます。そしてスコープから抜けるたびに、デストラクタが実行されます。
したがってループ内で使う変数はループの外側で宣言した方が効率がよいでしょう。
グローバル変数、スタティック変数、スタティッククラスメンバ変数、関数スタティック変数などを含む、静的記憶域期間をもつオブジェクトは、POD(Plain Old Data)でなくてはなりません。PODとは、int、char、float、ポインタ、および、PODの配列/構造体のみでできていることを意味します。
C++ではコンストラクタとスタティック変数のイニシャライザの呼び出し順序が部分的にしか決められておらず、ビルドするたびに変わってしまう可能性があります。このことは発見困難なバグを引き起こすおそれがあります。したがってクラス型のグローバル変数を使うこと、そしてスタティックなPOD変数を関数の結果で初期化することを禁止します。ただし(getenv() や getpid() のように)関数が他のグローバル変数に依存している場合にはその限りではありません。
またデストラクタの呼び出し順序は、コンストラクタの呼び出し順序の逆順だと定義されています。コンストラクタの呼び出し順序が不定なので、デストラクタの呼び出し順序も不定になります。したがって、たとえばプログラム終了時にスタティック変数が破壊されたのに、(おそらく別のスレッドで)動いているコードがその変数にアクセスしようとして失敗する可能性があります。スタティックな「文字列」変数のデストラクタが、その文字列へのリファレンスを含む別の変数のデストラクタよりも先に呼び出されるおそれもあります。
結局のところ、スタティック変数はPODデータを含むときにしか使えないということです。vector
や string
も許されません(代わりに、Cの配列や const char*
を使ってください)。
もしクラス型のスタティック変数やグローバル変数が必要であれば、main() 関数か pthread_once() 関数で(決して free されない)ポインタを初期化することを検討してください。これはスマートポインタではなく、単なるポインタでなくてはならないことに注意してください。スマートポインタのデストラクタにも、これまで説明したのと同様、デストラクタの呼び出し順序の問題があるためです。
Init()
メソッドを使うべきです。
main()
関数よりも先に呼び出されてしまいます。これはコンストラクタのコードにとっておそらく想定外でしょう。
たとえば gflags はまだ初期化されていない状態になります。
Init()
メソッドを用意することを検討してください。特にコンストラクタでは、仮想関数を呼び出したり、エラーを発生しようとしたり、初期化されないおそれのあるグローバル変数にアクセスすべきではありません。
new
すると、デフォルトコンストラクタが呼び出されます。 new[]
したときも同様に、デフォルトコンストラクタが呼び出されます(配列の場合)。
クラスにメンバ変数を定義していて他にコンストラクタがないときには、デフォルトコンストラクタ(引数をとらないコンストラクタ)を定義しなければなりません。オブジェクトはできる限り矛盾がなく有効な内部状態になるよう初期化するべきです。
こうしたのは、他にコンストラクタがないのにデフォルトコンストラクタを定義していない場合、コンパイラがコンストラクタを生成してしまうためです。コンパイラが生成したコンストラクタはオブジェクトをうまく初期化できないおそれがあります。
もし既存のクラスから継承したクラスで、そこに新たなメンバ変数を追加していないのなら、デフォルトコンストラクタを用意する必要はありません。
explicit
キーワードを付けてください。
Foo::Foo(string name)
が定義してあるとします。Foo
が渡されることを期待している関数に文字列を渡すと、このコンストラクタが呼び出されて文字列を Foo
に変換します。そして関数には Foo
が渡されるのです。これは便利なのですが、この変換によって暗黙のうちに新しいオブジェクトが生成されることはトラブルの原因にもなり得ます。コンストラクタに explicit
を宣言しておけば、暗黙のうちに変換されるのを防ぐことができます。
1つの引数をとるコンストラクタにはすべて、explicit を明示してください。クラス定義にある引数が1つのコンストラクタの前には、必ず explicit
を付けてください。
explicit Foo(string name);
ただしコピーコンストラクタは例外です。こういうケースはめったにありませんが、explicit
を付けるべきではないでしょう。
他のクラスに対する透過的なラッパーとして使うクラスも例外です。
こうした例外ケースの場合には、その旨コメントに明記しておくべきです。
DISALLOW_COPY_AND_ASSIGN
を使って無効にしてください。
CopyFrom()
というスタイルよりも効率がよくなります。コピーコンストラクタはコピーの解釈も兼ねており、コンテキストに応じてコピーを省略し、ヒープ割り当てを避けることができるためです。
ほとんどのクラスはコピー可能である必要はなく、コピーコンストラクタも代入演算子も持つべきではありません。たいていの場合、ポインタやリファレンスを使うことで、コピーした値と同様の振る舞いをパフォーマンスよく実現できます。たとえば関数パラメータには、値ではなくリファレンスやポインタを渡すことができます。またSTLコンテナには、オブジェクトではなくポインタを格納することができます。
もしクラスをコピー可能にする必要があるなら、コピーコンストラクタよりも CopyFrom()
や Clone()
といったコピーメソッドを提供する方がよいです。こうしたコピーメソッドは暗黙に呼び出されることがないためです。コピーメソッドでは不十分な場合(たとえばパフォーマンスのためや、そのクラスをSTLコンテナの値として格納する必要がある場合)には、コピーコンストラクタと代入演算子の両方を提供してください。
もしクラスにコピーコンストラクタや代入演算子が必要ないなら、明示的に無効にしなくてはなりません。
これを実現するため、クラスの private:
セクションにコピーコンストラクタと代入演算子のダミー宣言を追加してください。これらに対応する定義を提供してはいけません(こうしておくと、使おうとしたときにリンクエラーが発生します)。
以下のような DISALLOW_COPY_AND_ASSIGN
マクロを使うと便利です。
class Foo
内で以下のようにします。
struct
はデータを運ぶ受動的なオブジェクトだけに使ってください。それ以外にはすべて class
を使ってください。
C++の場合、キーワード struct
と class
はどちらもほぼ同じように動作します。そこで私たちはこれらのキーワードに意味を持たせました。定義するデータの種類に合わせてキーワードを使い分けるべきです。
struct
はデータを運ぶ受動的なオブジェクトに使うべきです。定数と関連付けても構いませんが、データメンバの取得/設定以外の機能を持たせてはいけません。フィールドの取得/設定には、メソッド呼び出しではなくフィールドに直接アクセスすることで実現します。メソッドは動作を提供するのではなく、データメンバの設定にのみ使われるべきです。つまり、コンストラクタ、デストラクタ、Initialize()
、Reset()
、Validate()
といったメソッドのみにしてください。
これ以上の機能が必要な場合には、class
を使う方が適切です。はっきりしなければ、class
を使ってください。
STLと一貫性を持たせるため、functor や traint には class
ではなく struct
を使っても構いません。
構造体とクラスのメンバ変数には異なる命名規則があることに注意してください。
public
継承にしてください。
継承はすべて public
継承にするべきです。プライベート継承を使いたければ、代わりにベースクラスのインスタンスをメンバとして持たせるべきです。
実装の継承を使いすぎてはいけません。実際のところ、コンポジションを使った方ががよい場合が多いです。継承を使うのは、"is-a" 関係があるときだけにしましょう。もし「Bar
は Foo
の一種(a kind of)である」と無理なく言えるなら、Bar
を Foo
のサブクラスにしても構いません。
必要に応じてデストラクタを virtual
にしましょう。クラスに仮想メソッドがあるなら、そのクラスのデストラクタは virtual
にすべきです。
protected
を使うのは、サブクラスからアクセスする必要のあるメンバ関数だけに制限してください。データメンバはプライベートにすべきということに注意しましょう。
継承した仮想関数を再定義するときには、派生したクラスの宣言において virtual
と明示的に宣言してください。virtual
を省略してしまうと、その関数が仮想関数かどうか確認するには、そのクラスのすべての先祖をチェックしなくてはならないためです。
Interface
というサフィックスのついた純粋なインタフェースクラスである場合に限り、多重継承を使っても構いません。
Interface
というサフィックスを付けなくてはなりません。
Interface
というサフィックスを付けることができます。ただし必須ではありません。
クラスが次の条件を満たす場合、それは純粋なインタフェースだと言えます。
= 0
")メソッドとスタティックメソッドしかない(ただしデストラクタについては以下を参照)。
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:
セクションという順にすべきです。セクションが空の場合には省略します。
各セクション内では通常、次の順序で宣言するべきです。
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
shared_ptr<const T>
)。非constなリファレンスでリファレンスカウントされたポインタであればうまくいく場合もありますが、できる限り単一オーナーになるよう書き直しましょう。
cpplint.py
を使ってスタイルのエラーを検出してください。
cpplint.py
はソースファイルを読み込み、スタイルに関するいろいろなエラーを検出するツールです。ツールも完全ではなく、誤検出もあれば検出漏れもありますが、それでも十分役に立ちます。誤検出の場合には、その行末に // NOLINT
を入れると無視してくれます。
プロジェクトで使っているツールから cpplint.py
をどう実行するのか、きちんと説明されているプロジェクトもあります。あなたが参加しているプロジェクトにそういう説明がなくても、個別に cpplint.py
をダウンロードして使えます。
const
を付けなければなりません。
int foo(int *pval)
など。C++では別の方法として、リファレンスパラメータを宣言できます。int foo(int &val)
(*pval)++
といった見苦しいコードを避けられます。コピーコンストラクタなどにもリファレンスが必要になります。ポインタとは違い、リファレンスを使うと NULL
といった値が取り得ないことを明確にできます。
関数パラメータのリストでは、すべてのリファレンスに const
を付けなければなりません。
実際のところ「入力パラメータは値もしくは const
リファレンスとし、出力パラメータはポインタとする」というのが、Googleのコードにおける非常に厳格な規則となっています。入力パラメータに const
ポインタを使っても構いませんが、非 const
のリファレンスを使うことは許されません。
しかし入力パラメータの const T&
に const T*
を使うのが適切な場合もあります。たとえば、
NULLで渡したい、
その関数は入力に対するポインタやリファレンスを保持する、といった場合です。
たいていの場合、入力パラメータは const T&
になることを覚えておきましょう。const T*
を使うときには、その入力は通常とは異なる扱いをすることをコードを読む人に伝えてください。const T&
ではなく const T*
を選ぶときには、具体的な理由を書いてください。そうしないとコードを読む人はありもしない理由を探して混乱してしまいます。
const string&
を引数とする関数を書き、それを const char*
を引数とする別の関数でオーバーロードできます。
Append()
ではなく、AppendString()
や AppendInt()
とするとよいでしょう。
よく理解せずにそのままデフォルトをプログラマに受け入れさせるのでなく、そのAPIと引数として渡している値についてよく考えさせるよう、すべての引数を明示的に指定してください。ただし、以下の場合はその限りではありません。
可変長引数リストをシミュレートするためにデフォルト引数を使うのは例外とします。
alloca()
を使ってはいけません。
alloca()
はどちらも非常に効率がよいものです。
scoped_ptr
/scoped_array
などの安全なアロケータを使ってください。
friend
クラスや friend
関数を使っても構いません。
フレンドは通常、同じファイルに定義すべきです。そうしておけば、コードを読む人が他のファイルの中身を見なくても、クラスのプライベートメンバが使われている場所を見つけることができます。friend
がよく使われるのは、FooBuilder
クラスを Foo
のフレンドにするというものです。これにより、Foo
の内部状態を全員に公開することなく、FooBuilder
クラスは正しくオブジェクトを構築することができます。ユニットテストのクラスを、テスト対象となるクラスのフレンドにしておくと便利な場合もあります。
フレンドはクラスのカプセル化の範囲を拡げるものですが、カプセル化を破るものではありません。ある他のクラスだけからアクセスできるようにしたい場合には、メンバをパブリックにするよりも都合がよい場合もあります。しかし、たいていの場合、クラスはパブリックメンバを介して別のクラスとやり取りするべきです。
Init()
メソッドを使うことでシミュレーションも可能ですが、その場合にはヒープ割り当てや新たな「不正な」状態といったものが必要になります。throw
文を追加するときには、途中にある呼び出し元をすべて調査しなければなりません。少なくとも、基本的な例外の安全性を保証するか、例外を捕捉せずに結果としてプログラムが中断してしまうことを良しとしなければなりません。たとえば f()
が g()
を呼び出し、g()
が h()
を呼び出しており、h
が f
によって捕捉される例外を投げているなら、g
には注意が必要があります。適切にクリーンナップされないおそれがあります。表面的には、例外を使うメリットはそのコストを上回ります。新規プロジェクトの場合にはなおさらです。しかし既存のコードに例外を導入しようとすると、依存関係のあるコードすべてに影響が及びます。もし例外が新規プロジェクトを超えて伝播する可能性があるなら、新規プロジェクトを例外を使っていない既存のコードに組み込むときに問題となります。Googleの既存のC++コードの多くは、例外を処理するように作られていません。したがって、例外を発生させるような新規コードを導入するのは、多少難しいことです。
Googleの既存コードに例外に対する耐性がないことを考慮すると、Googleのプロジェクトで例外を使うのは、新規プロジェクトで例外を使うのと比べて、ややコストが大きいと言えます。例外の変換プロセスには時間がかかり、問題になりやすいものです。また私たちはエラーコードやアサーションといった例外の代替手段が大きな障害になるとは考えていません。
例外を使うことに対する反対意見には、賢明あるいは高潔な理由があるわけではありません。これは現実的な理由からです。 Googleではオープンソースプロジェクトを積極的に利用したいと考えていますが、ここで説明した理由から、例外を使っているプロジェクトを採用するのは難しくなっています。したがって、Googleのオープンソースプロジェクトにおいても例外に対して反対意見を述べざるを得ません。 スクラッチから再度全部やり直せるなら、おそらく考えは違っていたでしょう。
Windowsのコードについては、このルールは例外です(シャレでなく)。
RTTIはユニットテストに便利です。たとえばファクトリクラスをテストするとき、新しく生成されたオブジェクトが期待通りの型になっているか確認するのに使えます。
めったにないことですが、テスト以外にも役立つことがあります。
ユニットテスト以外でRTTIを使ってはいけません。オブジェクトのクラスによって異なる動作をするようなコードを書く必要があれば、別の方法で型を問い合わせることを検討しましょう。
サブクラスの型によって異なるコード実行パスをとりたいときには、仮想メソッドを使うとよいでしょう。それぞれの処理をそれぞれのオブジェクト内部に置くことができます。
現在処理しているコードではなくオブジェクトの外部に処理があるときには、Visitorデザインパターンのようなダブルディスパッチというテクニックの利用を検討しましょう。ダブルディスパッチを使うと、組み込みの型システムを使ってオブジェクトの外部でクラスの型を決定できます。
どうしてもこうしたアイデアが使えないのなら、RTTIを使っても構いません。ただし、そうする前にもう一度考えてください。:-) そして、もう一度考えてください。 RTTIに似たような回避策を自前で実装するのもいけません。型タグを持つクラス階層などの回避策にも、RTTIに対する議論がほとんどそのまま当てはまります。
static_cast<>()
といった、C++のキャストを使ってください。int y = (int)x;
や int y = int(x);
といった書式のキャストを使ってはいけません。
(int)3.5
)、キャストが行われることもあります(たとえば (int)"hello"
)。C++のキャストを使うと、こうした不明確さを避けられます。さらに、どこでキャストが使われているのか探すときにもわかりやすくなります。
Cスタイルのキャストを使ってはいけません。代わりにC++スタイルのキャストを使ってください。
static_cast
を使ってください。
const
修飾子(constを参照)を取り除きたいときには、const_cast
を使ってください。
reinterpret_cast
を使ってください。ただし使うのは、何をしているのかわかっていて、エイリアス問題についても理解しているときだけにしてください。
dynamic_cast
を使ってはいけません。
ユニットテスト以外で実行時に型情報を知る必要があるなら、おそらく設計上の欠陥があるのでしょう。
printf()
や scanf()
の代わりになります。
printf
でも問題は起こりません)。ストリームには自動のコンストラクタとデストラクタがあり、ファイルのオープンとクローズを自動的にやってくれます。
pread()
のような動作を実現するのは困難です。printf
のようなハックを使わずにストリームを使って効率よくやるのは不可能ではありませんが、書式が複雑になるおそれがあります(具体的には、よく見かける文字列イディオム %.*s
など)。ストリームは国際化に便利な演算子の並び換え(%1s
ディレクティブ)をサポートしていません。
ログ記録のためのインタフェースとして必要な場合を除き、ストリームを使ってはいけません。代わりに printf
などを使ってください。
ストリームの使用については賛否両論あります。いずれにせよ、この場合も一貫性を保つことが最優先です。コードにストリームを使ってはいけません。
この問題には長い議論があったので、理由についてさらに詳しく説明しておきます。たったひとつの指針を思い出してください。あるI/O型を使うときはいつでも、コードのどこであっても同じように見えるようにしたいのです。したがって、ストリームを使うのか read/write や printf
を使うのか、ユーザが各自決められるようにしたくはありません。どちらか1つに決めるべきなのです。ただしログ記録については例外としました。これはかなり特殊な用途であり、歴史的な理由もあるためです。
ストリームの支持者らはストリームが選ばれるのは疑う余地がないと主張しましたが、実際のところ問題はそれほど明快ではありません。彼らが挙げるストリームの利点に対して、同じだけの欠点を挙げることができます。ストリームの最大の利点は、プリントするのにオブジェクトの型を知る必要がないことです。これには疑う余地はないでしょう。しかしここにはマイナス面もあります。間違った型を使ってしまいやすく、たとえ間違っていてもコンパイラは警告してくれません。ストリームを使うと、知らず知らずのうちに次のような間違いをしやすくなります。
このときコンパイラはエラーを吐いてくれません。<<
がオーバーロードされているためです。これはオーバーロードを使うことを推奨していない理由でもあります。
printf
は不恰好で読みにくいという人もいますが、ストリームにも同じことが言えるでしょう。次の2つのコードを見てみましょう。どちらにも同じ typo があります。どちらの方が見つけやすいですか?
他にもいろいろと問題を挙げることができます。(あなたは「ラッパーを使えば改善できる」と主張するかもしれませんが、あるスキーマで正しいからといって他のスキーマでも正しいわけではありませんよね? それに私たちの目標は言語をもっと小さくすることであって、学習しなければならない仕組みをさらに増やすことではありません。)
どちらをとっても異なる利点と欠点があり、明らかに優れた解決策というものはありません。それでもシンプルさの原則にしたがって、どちらか1つに来めなくてはなりません。そして最終的に多数決により、printf
+ read
/write
が採用されたわけです。
++i
)で使ってください。
++i
や i++
)デクリメントしたり(--i
や i--
)し、その式の値を使わない場合、前置インクリメント(デクリメント)と後置インクリメント(デクリメント)のどちらを使うか選択する必要があります。
++i
)を使うことで「後置」形式(i++
)より効率が落ちることはありません。実際のところ効率がよくなることが多いです。後置インクリメント(またはデクリメント)の場合には、i
のコピー(これが式の値になる)を作る必要があるためです。i
がイテレータや非スカラ型の場合には、i
をコピーするコストは高くついてしまいます。
値が無視されるときにはどちらも同じような動作をします。前置インクリメントだけにしない理由はありますか?
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
を最初に置くことを推奨しますが、必須ではありません。ただし、まわりのコードと一貫性を持たせてください。
int
だけです。別のサイズの変数が必要であれば、<stdint.h>
にある int16_t
といった固定幅の型を使ってください。
short
は16ビット、int
は32ビット、long
は32ビット、long long
は64ビットになります。
<stdint.h>
には int16_t
、uint32_t
、int64_t
といった型が定義されています。
short
、unsigned long long
を使う代わりに、必ずこれらの型を使うべきです。Cの整数型で使ってもいのは int
だけにすべきです。必要があれば size_t
や ptrdiff_t
といった標準型を使っても構いません。
ループカウンタなどあまり大きくならないとわかっている整数には、int
を使うことが非常に多いです。こうした場合には昔からある int
をそのまま使いましょう。int
は少なくとも32ビットあると考えても構いませんが、32ビット以上あると考えてはいけません。
もし64ビットの整数型が必要であれば、int64_t
や uint64_t
を使ってください。
「大きくなる」ことがわかっている整数には、int64_t
を使ってください。
表現しているのが実際には数値ではなくビットパターンであったり、2の補数オーバーフローを定義する必要があるときを除いて、uint32_t
といった unsigned 整数型を使うべきではありません。特に数値が負にならないことを示したいだけのために unsigned 型を使ってはいけません。代わりにアサーションを使ってください。
教科書を書いている人のなかには、負にならない数値を表現するのに unsigned 型を使うことを推奨している人たちもいます。これは自己文書化のためです。しかしCの場合には、実際のところ文書化するメリットよりもバグを引き起こすデメリットの方が上回ります。次の例を考えてみましょう。
このコードは止まりません。gccはこの種のバグに気づいて警告してくれることもありますが、気づいてくれないことも多々あります。signed と unsigned を比較するときにも同じくらいひどいバグを引き起こすおそれがあります。つまり、Cの型昇格の仕組みによって、unsigned 型は期待と異なる動作をするおそれがあるのです。
したがって、変数が負でないことを示すにはアサーションを使ってください。unsigned 型を使ってはいけません。
printf()
の指定子には、32ビットシステムと64ビットシステムとの間で移植性がはっきりしないものがあります。C99には移植性のある書式指定子がいくつか定義されています。しかし残念ながら、MSVC 7.1はこうした指定子のいくつかを理解できず、標準とはなっていません。したがって自分で不恰好なバージョンを定義しなくてはならない場合もあります(標準のインクルードファイル inttypes.h
のスタイルで)。
型 | 使ってはいけない | 使ってもよい | 注意 |
---|---|---|---|
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())
が使えます。
LL
や ULL
というサフィックスを使ってください。たとえば次のようになります。
#ifdef _LP64
を使ってコードを分岐させてください。(できるだけこうするのを避け、変更を局所的にしましょう。)
const
変数を使った方がよいです。
マクロを使うということは、あなたが見ているコードとコンパイラが見ているコードが違うということです。これは予期せぬ動作を引き起こすことがあります。特にマクロがグローバルスコープにあるときに問題になりやすいです。
幸運なことに、C++ではCほどマクロを必要としません。パフォーマンスに影響を及ぼすコードをインライン化するときには、マクロではなくインライン関数を使ってください。定数を格納するときには、マクロではなく const
変数を使ってください。長い変数名を省略するときには、マクロではなくリファレンスを使ってください。コードを条件付きコンパイルするときには、マクロではなく、いや、そもそもそんなことをしてはいけません(もちろんヘッダファイルの二重インクルードを防ぐための #define
ガードは除きます)。そんなことをすると、テストが非常に難しくなります。
こうした他のやり方ではできない場合には、マクロを使っても構いません。コードベース、特に低レベルのライブラリではよく見かけるでしょう。また特殊な機能(文字列化、連結など)のいくつかは、言語によってはうまく使えません。しかしマクロを使う前には、マクロを使わずに同じ結果を達成する方法がないか注意深く検討してください。
以下の利用パターンにしたがうと、マクロを使ったときの問題の多くを回避できます。マクロを使うときにはできるだけ以下にしたがってください。
.h
ファイルにマクロを定義してはいけません。
#define
マクロを置き、使った直後に #undef
してください。
#undef
してはいけません。その代わりに、ユニークな名前を持つマクロを使ってください。
##
を使うのは望ましくありません。
0
を、実数には 0.0
を、ポインタには NULL
を、文字には '\0'
を使ってください。
整数には 0
を使い、実数には 0.0
を使ってください。これには賛否両論あります。
ポインタ(アドレス値)には、0
と NULL
という選択肢があります。Bjarne Stroustrup氏は簡素な 0
の方を好んでいます。私たちは NULL
の方が好みです。こちらの方がポインタらしく見えるためです。実際のところC++コンパイラには gcc 4.1.0 のように、有用な警告をするために NULL
に特別な定義付けをしているものもあります。具体的に言うと、sizeof(NULL)
が sizeof(0)
と等しくない状況で警告してくれます。
文字には '\0'
を使ってください。
これは型としても正しく、コードも読みやすくなります。
sizeof(型)
ではなく sizeof(変数名)
を使ってください。
sizeof(変数名)
を使ってください。こうすると変数の型を変更してもうまく更新されるためです。
sizeof(型)
の方がふさわしい場合もありますが、一般的には避けるべきです。変数の型を変更すると一致しなくなるおそれがあるためです。
コードを読んでメンテナンスするコントリビュータ全員にとって読みやすくなるよう、私たちはBoostライブラリの一部のみを許可することにしました。現在のところ、以下のライブラリが許可されています。
boost/call_traits.hpp
)
boost/compressed_pair.hpp
)
boost/ptr_container
)ただし、シリアライゼーションとC++03標準でないコンテナのためのラッパー(ptr_circular_buffer.hpp
と ptr_unordered*
)を除く。
boost/array.hpp
)
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
私たちはこれ以外のBoost機能のリスト追加についても引き続き検討しています。したがって、このルールは将来緩和されるかもしれません。
C++11標準はこれまで以上に複雑で(800ページが1,300ページに)、まだ開発者に広く知られてはいません。コードの読みやすさやメンテナンスに関して長期的に効果があるのか、まだ未知数です。こうした機能がいつ各種ツール(gcc、icc、clang、Eclipseなど)に実装されるのか、まだわかりません。
Boost 同様、C++11拡張には読みやすさの妨げになるコーディングプラクティスを促すものがあります。たとえばコードを読む人にとって役立つチェックの冗長性(型名など)の省略や、テンプレートメタプログラミングの助長といったものです。また既存の仕組みでも実現可能な機能と重複する拡張もあり、混乱と変換コストをもたらすおそれがあります。
一貫性を保つ上で最も重要なルールは命名に関するものです。命名スタイルがあると、名付けられたものが何なのか、型や変数、関数、定数、マクロなのか、などがすぐにわかります。宣言をあちこち探す必要もなくなります。頭の中のパターンマッチングエンジンは、命名規則を当てにしているのです。
命名規則にはかなり好みがありますが、私たちは個人の好みよりも一貫性を持つことの方が重要だと考えています。したがって、それが理にかなっていると感じても感じなくても、決まりは決まりだと考えてください。
無理のない範囲で、できるだけわかりやすい名前にしてください。スペースを節約しようとしてはいけません。新たにコードを読む人がすぐに理解できる方が、はるかに重要なことです。よい名前の例を以下に挙げます。
よくない名前とは、曖昧な省略や意味のない、いい加減な言葉を使うことです。
型と変数の名前は通常、名詞にするべきです。たとえば FileOpener
、num_errors
など。
関数名は通常、命令的なものにすべきです(つまり「コマンド」にすべきです)。たとえば OpenFile()
、set_num_errors()
など。アクセサはこの限りではありません。アクセスする変数と同じ名前にしてください。関数名のところで詳しく説明します。
プロジェクト外でも極めてよく知られている略語でない限り、使ってはいけません。例:
文字を削って短縮してはいけません。
_
)やダッシュ(-
)を含んでも構いません。プロジェクトで採用している規則にしたがってください。したがうべき一貫性のあるパターンがなければ、"_" を使うとよいでしょう。
許されるファイル名の例。
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.h
と foo_bar.cc
という2つのファイルを使います。
インライン関数は .h
ファイルに入れなければなりません。簡潔なインライン関数は .h
ファイルに直接書くべきです。大きなインライン関数は -inl.h
で終わる別のファイルに入れても構いません。インラインコードがたくさんあるクラスは3つのファイルで構成されることになります。
-inl.hファイルのセクションも参照してください。
MyExcitingClass
、MyExcitingEnum
など。
すべての型、— クラス、構造体、typedef、enum — の名前には、同じ命名規則を使います。型名は大文字で始めて、単語の先頭を大文字にするべきです。アンダースコアは使いません。例:
my_exciting_local_variable
、my_exciting_member_variable_
など。
例:
データメンバ(インスタンス変数やメンバ変数とも呼ばれる)は通常の変数名と同様、アンダースコアを含む小文字にします。ただし、必ずアンダースコアで終わってください。
構造体のデータメンバはクラスのデータメンバにある末尾のアンダースコアを除いて、通常の変数と同様に命名するべきです。
構造体とクラスの使い分けについては、構造体対クラスを参照してください。
グローバル変数に特別な決まりはありません。グローバル変数はめったに使うべきではありませんが、もし使うのであれば g_
などのプレフィックスを付けてローカル変数と区別しやすくするよう検討しましょう。
k
で始まり、大文字小文字で続けてください。たとえば kDaysInAWeek
など。
コンパイル時定数は、それがローカルであろうとグローバルであろうと、クラスの一部であろうとなかろうと、どのように宣言されていたとしても、他の変数とは全く異なる命名規則にしたがってください。k
の後に単語の先頭を大文字にして続けます。
MyExcitingFunction()
,
MyExcitingMethod()
,
my_exciting_member_variable()
,
set_my_exciting_member_variable()
関数名は大文字で始めて、単語の先頭を大文字にするべきです。アンダースコアは使いません。
もし関数がエラー時にクラッシュするなら、関数名にOrDieを付加すべきです。これは製品コードで使われる可能性のある関数と通常のオペレーションで適度に発生するおそれのあるエラーにのみ適用されます。
アクセサとミューテータ(get/set 関数)は取得および設定しようとする変数名と一致させるべきです。以下は num_entries_
というインスタンス変数を持つクラスの一部を示しています。
非常に簡潔なインライン関数には小文字を使っても構いません。たとえばループ内でその関数を呼び出しても値をキャッシュしない非常に簡単なものであれば、小文字で命名しても構いません。
google_awesome_project
など。
名前空間とその命名については、名前空間を参照してください。
kEnumName
や ENUM_NAME
とします。
できるだけ列挙子は定数と同じようにすべきですが、マクロ と同じようにしても構いません。列挙型名 UrlTableErrors
(および AlternateUrlTableErrors
)は型なので、大文字と小文字を組み合わせた名前にしてください。
2009年1月までは、enum値はマクロと同じように命名するスタイルでした。ところがこれにより、enum値とマクロの名前が衝突するという問題が発生しました。その結果、定数と同じスタイルで命名する方がよいと変更されました。新規のコードではできるだけ定数スタイルで命名すべきです。ただし、古い命名でも実際のコンパイル時に問題が発生していなければ、そのままで構いません。
MY_MACRO_THAT_SCARES_SMALL_CHILDREN
マクロの説明を参照しましょう。一般的にマクロは使うべきではありません。どうしても必要なら、大文字とアンダースコアだけを使った名前にするべきです。
bigopen()
open()
の形にする。uint
typedef
bigpos
struct
や class
。 pos
の形にしたがう。sparse_hash_map
LONGLONG_MAX
INT_MAX
と同様。コメントを書くのは苦痛ですが、コードを読みやすくするのに不可欠です。以下のルールはどこにどんなコメントを書くべきか説明しています。 ただし忘れないでください。コメントはとても重要ですが、最高のコードとは自己文書化されたコードのことです。曖昧な名前を付けてコメントで説明しようとするより、型や変数に意味のある名前を付けておく方がはるかによいことなのです。
コメントはコードを読む人のために書いてください。つまりコードを理解する必要のある次のコントリビュータのためです。親切心をもってください — 次のコントリビュータはあなた自身かもしれません。
//
もしくは /* */
、どちらかを、一貫性を持って使ってください。
//
と /* */
、どちらの構文を使っても構いません。一般的には //
の方がかなりよく見かけます。
どのようにコメントするか、どんなスタイルでどこにコメントするかには、一貫性を持たせてください。
すべてのファイルに以下の項目を以下の順序で入れるべきです。
Copyright 2008 Google Inc.
)もともと他人が書いたファイルに大きな変更をした場合には、作者のところに自分の名前を追加しましょう。これは他のコントリビュータがそのファイルについて質問をしたり、問い合わせ先を知る必要があるときに非常に役立ちます。
各ファイルの先頭(コピーライト宣言と作者に関する行の下)には、ファイルの内容に関するコメントを入れるべきです。
.h
ファイルには通常、そのファイルで宣言しているクラスがどんなものでどのように使うのか、その概略を説明しておきます。.cc
ファイルには、詳細実装やトリッキーなアルゴリズムの解説に関する詳細情報を入れておくべきです。.h
を読む人にも実装詳細やアルゴリズム解説が役に立つと思うのであれば、それらを .h
に入れても構いません。ただしその場合、.cc
ファイルにはドキュメントが .h
にある旨明記してください。
.h
と .cc
との間でコメントをコピーしてはいけません。コピーするとコメントが別々に枝分かれしていくためです。
クラスの詳細について、すでにファイルの先頭で説明しているなら、"See comment at top of file for a complete description"(「詳しくはファイル先頭のコメントを参照」)と書いても構いませんが、必ず何かしらコメントを入れるようにしてください。
クラスが想定する同期があれば、それについて書いておきましょう。クラスのインスタンスが複数のスレッドからアクセスされる可能性があるなら、マルチスレッドの利用に関するルールや不変条件について明記してあるか注意してください。
関数宣言にはすべて、その関数が何をするものであり、どうやって使うかを説明したコメントを、関数の直前に入れてください。このコメントは命令調(「ファイルをオープンしろ」)ではなく説明調(「ファイルをオープンする」)にすべきです。このコメントは関数について説明するものであって、関数が何をしているかを説明するものではありません。一般的に宣言のコメントというのは、関数がそのタスクをどうやっているかを説明するものではありません。そうしたコメントは関数宣言ではなく関数定義のコメントに入れるべきです。
関数宣言のコメントで言及すべきもの。
NULL
になるおそれがあるかどうか。
以下に例を挙げておきます。
必要以上に冗長にしたり、完全に明確に記述しようとしてはいけません。以下のように「さもなければ false を返す」と書く必要はありません。これは書かなくてもわかります。
コンストラクタとデストラクタにコメントを入れるとき、コードを読む人は何のコンストラクタとデストラクタか知っていることを覚えておきましょう。「オブジェクトを破棄する」というコメントは意味がありません。そのコンストラクタが引数をどう処理するのか(たとえばポインタのオーナーシップがある場合)、そのデストラクタは何を解放するのか、といったことをコメントに入れておきましょう。ささいなことをコメントに入れる必要はありません。デストラクタにヘッダコメントがないことも多いです。
関数定義には、その関数がやっていることと、もしあればトリッキーな箇所を説明するコメントを入れるべきです。たとえば関数定義のコメントでは、使っているコーディングテクニックを説明したり、処理ステップの概要を説明したり、なぜ他ではなくその実装方法を選んだのかを説明しましょう。たとえば、なぜ関数の前半ではロックを取得する必要があり、なぜ後半ではその必要がないのか、といったことを説明するとよいでしょう。
.h
や、どこかの関数宣言にあるコメントを繰り返すだけではいけないことに注意しましょう。その関数について簡単な要約を入れても構いませんが、コメントで重要なポイントは「どのようにしてそれをやるか」であるべきです。
クラスのデータメンバ(インスタンス変数やメンバ変数とも呼ばれる)には、何のために使われるか説明するコメントを入れるべきです。変数が NULL
や -1 といった特別な意味のある値を番人として扱うときには、そのことを明記しておきましょう。例:
データメンバと同様に、グローバル変数にはすべて、それが何であり何のために使われるのか説明するコメントを入れるべきです。例:
トリッキーな箇所や複雑なコードブロックがあれば、その直前にコメントを入れるべきです。例:
わかりにくい行には、行末にコメントを入れるべきです。行末のコメントは、コードからスペース2つ分空けるべきです。例:
ここにはそのコードが何をしているか説明するコメントと、関数が戻るときにエラーがログに記録済みであることを述べたコメントがあることに注意しましょう。
コメントが複数行にわたるときには、開始位置をそろえると読みやすくなります。
関数に NULL
やブーリアン、リテラル整数値を渡すときには、それが何であるかをコメントに書いたり、定数を使うことで自己文書化したコードにするよう検討するべきです。たとえば以下を比べてみましょう。
次のようにするとよいでしょう。
代わりに定数や自己文書化した変数を使っても構いません。
コード自体を説明しようとしてはいけません。あなた以上にC++についてよく知っている人がコードを読んでいるのだと考えましょう。たとえ相手があなたのやろうとしていることを知らなくてもです。
コメントは通常、ひとつの完結した文章として書くべきです。大文字を適切に使って、文末にはピリオドを付けてください。コード末尾のコメントなど、コメントが短くなればなるほど形式が失われがちです。しかしスタイルには一貫性を持たせるべきです。完結した文章の方が読みやすく、コメントが完結していれば未完成の思い付きではないことがある程度わかります。
コードレビュアーから「セミコロンを使うべきところでカンマを使っている」と指摘されるとイライラしますが、ソースコードの明確さ、読みやすさを高いレベルで保つことは極めて重要です。句読点や綴り、文法が適切であることは、それを実現する助けとなります。
TODO
コメントは、一時的なものや短期的な解決策、その場しのぎの不完全な箇所に使ってください。
TODO
にはすべて大文字の TODO
という単語を使い、それからその TODO
について一番説明できる人の名前やメールアドレスなどの識別子を入れるべきです。コロンはあってもなくても構いません。主たる目的は TODO
の書式に一貫性を持たせて、もっと詳しく説明できる人がわかるようにすることです。TODO
に書かれた人がその問題を修正することを約束するわけではありません。したがって、あなたが TODO
を書くとき、ほとんどの場合は自分の名前を書くことになるでしょう。
もし TODO
が「将来の日付に何かする」という形式であれば、具体的な日付が入っているか(「2005年11月までに修正」)、もしくは具体的なイベントが入っているか(「すべてのクライアントがXMLレスポンスを処理できるようになったらこのコードを削除すること」)を確認しましょう。
DEPRECATED
というコメントを付けてください。
すべて大文字の DEPRECATED
を含むコメントを書くことで、そのインタフェースがもはや推奨されないという印をつけることができます。このコメントはインタフェース宣言の前か同じ行に置きます。
DEPRECATED
の後には、括弧で自分の名前やメールアドレスなどの識別子を書いておきましょう。
推奨されない旨のコメントには、呼び出し側を修正するためのシンプルで明確な指示が含まれていなくてはなりません。C++の場合、推奨されない関数を新しいインタフェースを呼び出すインライン関数として実装できます。
インタフェースに DEPRECATED
という印をつけたからといって、魔法のように呼び出し側を変更してくれるわけではありません。推奨されないインタフェースを実際に使うのをやめさせたければ、呼び出し側を自分で修正するか、手伝ってくれる仲間を募る必要があるでしょう。
新規のコードには、推奨されないインタフェースの呼び出しを含むべきではありません。代わりに新しいインタフェースを使ってください。もし指示が理解できなければ、推奨されない旨を書いた人を見つけて、新しいインタフェースを使うの手伝ってもらうよう頼みましょう。
コーディングスタイルや書式にはかなり好みがありますが、全員が同じスタイルを使うとプロジェクトの見通しがよくなります。とはいえ書式のルールに合意してくれない人もいるでしょう。慣れるまで時間がかかるルールもあるでしょう。それでもプロジェクトのコントリビュータ全員が同じスタイルにしたがうことは重要です。これによって、全員のコードが読みやすく理解しやすくなるためです。
コードを正しい書式にするのに役立つよう、私たちは Emacs用設定ファイルを用意しています。
このルールには賛否両論ありますが、既存のコードのほとんどがこれを守っています。そして私たちは一貫性を持つことが重要だと思っています。
最長80文字とします。
例外: コメント行に80文字を超えるコマンド例やURL文字列が含まれる場合には、カット&ペーストが簡単できるよう80文字を超えても構いません。
例外: #include
文に80カラムを超える長いパスが含まれる場合。ただし、こんなものが必要にならないようにしましょう。
例外: #define
ガードは最大長を超えても気にする必要はありません。
ユーザに表示するテキストをソースコードにハードコードしてはいけません。たとえ英語であってもです。したがって非ASCII文字が使われることはまずないはずです。ただし非ASCII文字をコードに入れると都合がよい場合もあります。たとえば外部からのデータファイルをパースするようなコードの場合、データファイルのデリミタとして非ASCII文字をハードコードしても構いません。(ローカライズの必要がない)ユニットテスト用のコードに非ASCII文字が含まれている場合もよくあります。この場合にもUTF-8を使うべきです。UTF-8であれば、単なるASCII文字以上を処理できるほとんどのツールで解釈できます。
Hexエンコーディングを使っても構いません。これを使うことで読みやすくなる場合もあります。たとえば "\xEF\xBB\xBF"
はUnicodeのゼロ幅改行なし空白文字を意味していますが、ソースコードにUTF-8でこの通り書いても見えません。
インデントにはスペースを使います。コードにはタブを使ってはいけません。 タブキーを押したときにはスペースが入力されるようエディタを設定しておきましょう。
関数は次のようになります。
長すぎて1行に収まらない場合には、次のようにします。
最初のパラメータですら1行に収まらない場合には、次のようにします。
いくつか注意すべきポイントがあります。
関数が const
の場合、const
キーワードは最後のパラメータと同じ行に書くべきです。
未使用のパラメータがある場合には、関数定義で変数名をコメントアウトしてください。
関数呼び出しは次のような書式になります。
引数が1行に収まらない場合には、複数行に分割して、2行目以降を最初の引数にそろえるべきです。開き括弧の後や閉じ括弧の前にスペースを入れてはいけません。
関数に引数がたくさんある場合、コードが読みやすくなるのであれば、引数を1行ごとに書くことを検討しましょう。
関数シグニチャが長すぎて最大の行の長さに収まらないときには、すべての引数を2行目以降に書いても構いません。
else
キーワードは新しい行に書きましょう。
基本的な条件文には2つの書式が許されています。ひとつは括弧と条件の間にスペースを入れるもので、もうひとつはスペースを入れないものです。
スペースを入れない書式の方がよく見かけると思います。どちらでも構いませんが一貫性を持たせてください。もし既存のファイルを変更しているなら、そこで使われている書式を使いましょう。もし新規のコードを書いているなら、そのディレクトリやプロジェクトで使われている書式を使いましょう。よくわからなくて個人的な好みもないのであれば、スペースを入れないでおきましょう。
お好みで括弧の中にスペースを入れても構いません。
if
と開き括弧の間には、必ずスペースを1つ入れる必要があることに注意しましょう。中括弧を使うときには、閉じ括弧と中括弧の間にもスペースを1つ入れなければなりません。
その方が読みやすいのであれば、短い条件文を1行にまとめて書いても構いません。ただしこれが許されるのは、その行が簡潔であり、else
節がないときだけです。
if文に else
がある場合には許されません。
一般に1行しかない文に中括弧を付ける必要はありませんが、中括弧を使うのが好みであれば付けても構いません。複雑な条件や文を含んだ条件文やループ文の場合には、中括弧を使った方が読みやすいかもしれません。if
には必ず中括弧を付けなければならないとしているプロジェクトもあります。
ただし if
-else
文で中括弧を使っているところが1箇所でもあれば、他のところにも中括弧を使わなければなりません。
{}
か continue
を使うべきです。
switch
の case
ブロックには、中括弧を付けても付けけなくても構いません。中括弧を付けるのであれば以下のようにするべきです。
条件が列挙値でなければ、switch文には必ず default
のケースを入れるべきです(列挙値の場合、値が処理されないおそれがあることをコンパイラが警告してくれます)。defaultのケースが起こり得ないときには assert
を書いておきましょう。
空ループの本体には {}
や continue
を使うべきです。セミコロンのみにしてはいけません。
ポインタとリファレンス表現の正しい書き方を以下に挙げます。
以下のことに注意してください。
*
や &
の後ろにスペースを入れてはいけない。
ポインタ変数やポインタ引数を宣言するときには、アスタリスクを型のそばに置いてもよいし、変数名のそばに置いても構いません。
ファイル内では一貫性を持たせるべきです。既存のファイルを変更するときはそのファイルのスタイルにしたがいましょう。
以下では論理積演算子を必ず行末に置いています。
この例ではコードを折り返すとき、行末に &&
論理積演算子を置いていることに注意しましょう。これはGoogleのコードでよく見かけますが、行頭に演算子を置くことも許されています。余分な括弧を入れても構いません。括弧をうまく入れると非常に読みやすくなります。また and
や compl
のようなワード演算子を使うのではなく、必ず &&
や ~
のような句読点演算子を使うべきであることに注意してください。
return
式を必要以上に括弧で囲んではいけません。
x = expr;
で括弧を使いたい場合に限り、return expr;
で括弧を使ってください。
=
を使ってもよいし、()
を使っても構いません。
初期化には =
を使ってもよいし、()
を使っても構いません。以下はすべて正しい書き方です。
たとえインデントされたコード本体のなかでも、プリプロセッサディレクティブは行の先頭から書き始めるべきです。
public
、protected
、private
の各セクションはこの順序で書いてください。これらのキーワードはスペース1つでインデントしてください。
クラス定義の基本となる書式は次のようになります(ここにはコメントを入れていませんが、どんなコメントが必要かについてはクラスのコメントを参照してください)。
以下のことに注意してください。
public:
、protected:
、private:
といったキーワードは、スペース1つでインデントすべき。
public
セクションが最初にきて、その後に protected
、そして最後に private
セクションがくるようにするべき。
イニシャライザリストには次の2つの書式が許されています。
あるいは、
名前空間内では余分なインデントをしてはいけません。たとえば次のようにしてください。
名前空間内では余分なインデントをしてはいけません。
ネストされた名前空間を宣言するときには、1行につき名前空間1つにしてください。
行末に空白が入っている場合、ほかの人が行末の空白を削除して同じファイルを編集すると、マージに余計な作業が発生するおそれがあります。したがって行末に空白を入れてはいけません。もし変更している行の末尾に空白があれば、その空白を削除してしまいましょう。あるいはクリーンナップ作業で削除してしまいましょう(できれば、だれもそのファイルで作業していないときに)。
これはルールというよりも原則です。不必要な空行を入れてはいけません。具体的に言うと、関数と関数の間に1行もしくは2行以上の空行を入れてはいけません。関数を空行で始めたくなるのをこらえましょう。関数を空行で終わらせてもいけません。関数内で空行を使うのは厳選しましょう。
基本原則: コードが1画面にうまく収まっていると、プログラムの制御フローが追いやすく理解しやすくなります。言うまでもないことですが、コードが密集しすぎても拡散しすぎても、読みやすくはなりません。自分自身で判断しましょう。一般的には垂直方向の空白の使用は最小限にしてください。
どんなときに空行を入れるとよいのか、次のような経験則があります。
ここで説明したコーディング規則は必須です。しかし、どんなに優れたルールにも例外はつきものです。ここではそうした例外について説明します。
このスタイルガイドにあるルールではなく、別のスタイルガイドにしたがって書かれたコードに手を加えることもあるでしょう。そうした場合には、ローカルなコード規則と一貫性を持たせるために、このガイドにあるルールから逸脱しなくてはならないかもしれません。疑問を感じたときには、コードの原作者や現在の責任者に尋ねましょう。一貫性にはローカルな一貫性も含まれていることを忘れてはいけません。
すでに広く蔓延しているWindowsスタイルを使っていたなら、このガイドラインに書かれたことを忘れているかもしれません。もう一度言っておきます。
iNum
という名前を付けるなど)。Googleの命名規則にしたがい、ソースファイルには .cc
という拡張子を付けてください。
DWORD
や HANDLE
など。Windows API関数を呼び出すときにはこれらの型を使っても構いませんし、むしろ使うことを推奨します。ただし、できる限り基本となるC++型を使ってください。たとえば LPCTSTR
ではなく const TCHAR *
を使いましょう。
#pragma once
を使ってはいけません。代わりに標準的なGoogleのインクルードガード(#define
ガード)を使いましょう。インクルードガードに使うパスはプロジェクトツリーのトップに対する相対パスにすべきです。
#pragma
や __declspec
といった非標準の拡張を使ってはいけません。__declspec(dllimport)
や __declspec(dllexport)
は使っても構いませんが、DLLIMPORT
や DLLEXPORT
といったマクロ経由で使いましょう。そうすればあとで簡単に無効化できます。
そうは言っても、Windowsの場合には時として違反せざるを得ないルールもあります。
_ATL_NO_EXCEPTIONS
を定義すべきです。STLを使うときにも、例外を無効にできないか調べるべきです。もしそれができなくても、コンパイラで例外を無効にできます。(これはSTLをコンパイルする場合に限ることに注意しましょう。例外を処理するコードを自分で書くべきではありません。)
StdAfx.h
や precompile.h
という名前になっています。ほかのプロジェクトとコードを共有しやすくするために、明示的にファイルをインクルードするのではなく(precompile.cc
の中を除く) /FI
コンパイルオプションでファイルを自動的にインクルードしましょう。
resource.h
という名前にして、マクロだけを含むようにしましょう。その内容については本スタイルガイドに準拠する必要はありません。
常識を働かせましょう、そして一貫性を持たせましょう。
コードを書いているところであれば、数分かけてまわりのコードを調べてスタイルを決めましょう。もし if
節のまわりに空白があれば、あなたもそうすべきです。もしコメントがスターで囲まれた小さなボックス状になっていれば、あなたのコメントもそうしましょう。
スタイルにガイドラインがあるということは、コーディングに共通のボキャブラリがあるということです。ガイドラインがあるおかげで、どう話すのかでなく、何を話すのかに集中できるようになります。さて、ここでは主にスタイルに関連したルールについて説明してきました。もう十分ボキャブラリについてはわかったはずです。ただし、ローカルなスタイルも重要だということを忘れないでください。あなたの書いたコードがまわりにある既存のコードと全然違って見えてしまうと、コードを読む人はバラバラに感じて、リズムが狂ってしまいます。そうなるのは避けましょう。
さて、コードの書き方についてはもう十分でしょう。コードを書く方がずっと楽しいのですから。さあ、楽しみましょう!
Revision 3.199
Benjy Weinberger