はじめに
プログラミングにおいて読みやすく間違いに気づきやすいコードを記述するための工夫がコーディングスタイルです。以下に示すようなスタイルにこだわる必要はありませんが、一貫したスタイルで記述されていないコードにはやはり問題があります。
既存のコードに対しては、そのコードのスタイルにしたがってください。スタイルを統一させることが大切です。自身のスタイルにこだわる必要はありません。
ここでは参考としてC++のコーディングスタイルを紹介します。GoogleのコーディングガイドラインおよびC++マニアックを参考にしました。
コメント
コメントがなくてもプログラムの処理の流れがわかるように適切な変数名・関数名を使用しましょう。
他の人がコードを読む手助けになるようなコメントを記述するようにします。実行される文の処理をそのまま記述したようなコメントには意味がありません。
空白文字
空行
空行はコードを読みやすくするために使用します。コードブロックの前後に空行を挿入します。
スペース
スペースはコードを読みやすくするために使用します。括弧の前後や演算子の前後にはスペースを挿入します。
インデント
コードの階層構造を分かりやすくするために階層ごとにスペース4文字(もしくは2文字)を行頭に挿入して字下げします。
識別子
識別子には意味のある英単語をできれば3語以上使用し、それぞれの単語はできるだけ省略しないようにします。
単語の区切り方には以下のようなものがあります。
| スネークケース(snake_case) | それぞれの単語を"_"(アンダースコア)で区切る(すべて小文字) |
| アッパースネークケース(UPPER_SNAKE_CASE) | それぞれの単語を"_"(アンダースコア)で区切る(すべて大文字) |
| キャメルケース(camelCase) | それぞれの単語の頭文字を大文字にする(先頭は小文字) |
| パスカルケース(PascalCase) | それぞれの単語の頭文字を大文字にする(先頭は大文字) |
| ケバブケース(kebab-case) | それぞれの単語を"-"(ハイフン)で区切る(すべて小文字) |
識別子の種類に応じて以下のように単語の区切り方を決めておきます。
| スネークケース(snake_case)で命名するもの | ファイル・変数・名前空間 |
| パスカルケース(PascalCase)で命名するもの | クラス・構造体(共用体)・関数・定義した型・列挙型 |
| アッパースネークケース(UPPER_SNAKE_CASE)で命名するもの | 記号定数・列挙型のメンバ・マクロ |
コメントに頼らなくても名前によって変数の役割や関数の機能がわかるようにします。関数名は「動詞+目的語」の形にします。ループのインデックスなど狭い範囲で使用する変数の名前はできるだけ短くして目立たないようにします。
プログラム全体で使用する定数には、コードが読みやすくなるように適切な名前をつけます。名前のつけられた定数を記号定数といいます。記号定数を使用することによってコードの可読性が向上するだけでなくメンテナンスも容易になります。1
よく使用される識別子には以下のようなものがあります。
| 識別子 | 意味 | 備考 |
| i, j, k | ループ変数 | index |
| n, num | 数値 | number |
| c, ch | 文字 | character |
| s, str | 文字列 | string |
| buf, buff | バッファ | buffer |
| tmp, temp | 一時的なもの | temporary |
| func | 関数 | function |
| arg | 引数 | argument |
| r | 半径 | radius |
| rect | 長方形 | rectangle |
| x | x座標 | |
| y | y座標 | |
| cx | 幅 | |
| cy | 高さ |
以下のようにプレフィクスをつけて変数の型を示すスタイル(いわゆるハンガリアン記法)は現在では推奨されていません。参考程度に留めてください。
クラス名および関数名にはそのクラスや関数の特徴に応じて以下のようなプレフィクスをつけます。
| 種類 | プレフィクス | 備考 |
| テンプレート | T | template |
| インターフェイス | I | interface |
クラス型の変数名にはそのクラスの種類に応じて以下のようなプレフィクスをつけます。
| クラス | プレフィクス |
| string | str |
| iterator | it |
| vector | vec |
| list | list |
| map | map |
| pair | pair |
変数名にはその変数のスコープや種類に応じて以下のようなプレフィクスをつけます。
| 種類 | プレフィクス | 備考 |
| メンバ変数 | m_ | member |
| 定数(const) | c_ | constant |
| 静的変数(static) | s_ | static |
| グローバル変数 | g_ | global |
変数名にはその変数の型に応じて(関数名にはその関数の戻り値の型に応じて)以下のようなプレフィクスをつけます。
| 型 | プレフィクス | 備考 |
| class | なし | |
| struct | st | structure |
| union | uni | |
| enum | e | enumerate |
| bool | b | |
| char | c | character |
| unsigned | u | |
| short | s | |
| long | l | |
| int | i | integer |
| float | f | |
| double | d | |
| [ ] (配列) | a | array |
| & (参照) | r | reference |
| * (ポインタ) | p | pointer |
| *func( ) (関数ポインタ) | pf | pointer of function |
| テンプレートの引数 | t | template |
| 定義した型 | tn | typename |
制御文
制御文は以下のようなスタイルで記述します。
if ( ... ) { if (false) { if (false if (true
... } else if ( ... ) { || ... && ...
} else { ... || ... && ...
... } else if ( ... ) { || ... && ...
} ... ) { ) {
} else { ... ...
... } }
}
switch ( ... ) { for (int i = 0; i < 10; ++i) {
break; case ... : ...
... }
break; case ... :
... while ( ... ) { do {
break; default: ... ...
... } } while ( ... );
}命令文が1行の場合でも{}は省略しないようにします。
命令文がない場合でもswitch文のdefaultは省略しないようにします。
ループ変数などループ内でしか使用しない変数は()内で宣言します。それらが複数ある場合はループに入る直前に宣言しても構いません。
宣言および定義
マクロ
必要な場合を除きマクロはできるだけ使用しないようにします。マクロが必要になるのは、ヘッダファイルを取り込んだり、インクルードガードしたり、条件付きコンパイルしたりするときです。
名前空間
名前の衝突を避けるため名前空間を適切に使用します。自分のコードを名前空間に含めると安全です。名前空間を無効にするusing namespaceやusing foo::barなどの使用は避けたほうがいいでしょう。
内部リンケージの指定には無名名前空間を使用します。
型およびキャスト
型の定義にはtypedefではなくusingを使用します。
型の名前が長いときや型の名前が重要でないと思われるときはautoを使用してもいいでしょう。
C言語形式のキャストを使用してはいけません。
定数
記号定数の定義にはdefineではなくconstexprを使用します。
ポインタ
生のポインタの使用は避けて参照やスマートポインタを使用します。アドレスを直接操作するのは危険です。
スマートポインタにはstd::unique_ptrを使用します。std::auto_ptrを使用してはいけません。
nullポインタにはNULLではなくnullptrを使用します。
関数
関数の定義はできるだけ短く記述します。40行くらいが目安です。1行に収まるのであればinline関数にしてもいいでしょう。
戻り値を()でくくる必要はありません。
クラス
暗黙の型変換を避けるため変換コンストラクタにはexplicitを指定します。
コピーしないクラスはoperator=およびコピーコンストラクタを非公開(private)にします。
基底クラスのデストラクタにはvirtualを指定(仮想関数として定義)します。
オーバーライドされるメンバ関数にはvirtualを指定します。オーバーライドするメンバ関数には(virtualではなく)overrideを指定します。
派生しないクラスおよびオーバーライドしないメンバ関数にはfinalを指定します。
// ヘッダファイル単体でコンパイルできるようにします。
// テンプレートやインライン関数などの定義は宣言と同じファイルに含めるようにします。
// /path/to/file/hello.hに対して以下のようなインクルードガードを記述します。
#ifndef PATH_TO_FILE_HELLO_H_
#define PATH_TO_FILE_HELLO_H_
#include <iostream>
namespace my_space {
// クラスの定義では [public] -> [protected] -> [private] の順にメンバを記述します。
//
// それぞれのセクションでは以下の順にメンバを記述します。
// [型] -> [定数] -> [ファクトリ関数] -> [コンストラクタ] -> [代入演算子] -> [デストラクタ] -> [その他のメンバ関数] -> [メンバ変数]
//
// メンバ関数の定義が長い場合はクラス定義の外側に記述します。
class MyClass {
public:
private:
};
} // namespace my_space
#endif // PATH_TO_FILE_HELLO_H_
// ヘッダファイルは以下の順でアルファベット順にインクルードします。
// Cのシステムヘッダファイル
#include <cstdio>
#include <cstring>
// C++のシステムヘッダファイル
#include <iostream>
#include <string>
// 他のライブラリのヘッダファイル
// 自身のプロジェクトのヘッダファイル
#include "my_header_file.h"
int main()
{
std::cout << "Hello, World!" << std::endl;
return EXIT_SUCCESS;
}-
コードに直接記述された値や文字列をリテラルといいます。意味の分からないリテラル(マジックナンバー)の使用は避けるべきです。 ↩︎