[ C++で開発 ]
C++コーディング標準はいくつかありますが、どうもピンとくるのがないので良いとこ取りで自分用コーディング標準をまとめます。
C++は、マルチパラダイム言語です。オブジェクト指向プログラミングを中心に据える場合はクラスが構成要素の中核となりますが、ジェネリックス(テンプレート)プログラミングを中心に据える場合はテンプレート集が構成要素の中核となります。もちろん構造化プログラミングを中心に据える場合はフリー関数が構成要素の中核となります。
主にオブジェクト指向プログラミングを行う場合のファイル構成法です。
クラス定義のレイアウトにはいくつか流儀があります。可視性制御順(public/protected/private)に並べるもの、意味順に並べるもの(構築系、アクセッサ系、ミューテータ系)、などです。ここでは、後者の意味順に並べる方法のうち、「なるほど、これはよい方法だ」とうなった記述方法を挙げます。
class Sample {
// コンストラクタ
public:
Sample();
Sample(Sample& r);
protected:
Sample(ResourceImpl* rimpl);
// デストラクタ
public:
virtual ~Sample();
// 操作
public:
void open();
void open(ResourceImpl* rimpl);
void close();
// 属性
public:
size_t getSize() const;
protected:
void setSize(size_t const size);
...
// 非実装提供
private:
Sample& operator=(Sample& rhs);
};
もし、よくあるpublic、protected、privateの順に定義するやり方でレイアウトしていたら、publicなメソッドをprivateに変更すると、定義行の位置が大きく変わり、バージョン管理システムでの差分が見えなくなるという問題がある。
※ 標準I/OストリームやSTLの命名とは異なる
| 命名項目 | お薦め規則 | |||
|---|---|---|---|---|
| 名前空間 | すべて小文字。単語間連結は直接。 | |||
| ユーザ定義型 class, struct, enum, typedef |
アッパーキャメルケース | |||
| メンバ関数名 | インスタンス | ローワキャメルケース | ||
| クラス(static) | ||||
| メンバ変数名 | インスタンス | ローワキャメルケースで末尾にアンダースコアを付加 | ||
| クラス(static) | ローワキャメルケースで末尾にアンダースコア2連を付加 | |||
| 引数名 | ローワキャメルケース | |||
| ローカル変数名 | ローワキャメルケース | |||
| グローバル関数名 | ||||
| グローバル変数名 | ||||
| ファイルスコープ関数名 | ローワキャメルケース | |||
| ファイルスコープ変数名 | ローワキャメルケース | |||
| マクロ定義名 | すべて大文字で単語間連結はアンダースコアを用いる | |||
class Simple {
int value;
explicit Simple(const Simple& rhs);
void operator=(const Simple& rhs);
public:
Simple();
~Simple();
};
コピーコンストラクタと代入演算子はコンパイラの自動生成が適用されないように明示的に定義する。ただし、クラスの設計上コピーコンストラクタと代入演算子を提供すると決定するまではprivateとしておく。
引数が1つのコンストラクタは、explicit宣言して暗黙的に変換されるのを抑制する。
Simple::Simple() : value(0) {
}
Simple::Simple(const Simple& rhs) {
value = rhs.value;
}
Simple::~Simple() {
}
void Simple::operator=(const Simple& rhs) {
if (this == &rhs) {
return;
}
value = rhs.value;
}
定数は、定数が使用される範囲のもっとも小さい(局所的な)スコープに定義する。
| #defineで定数を定義して使用しない。defineマクロは、型安全性とnamespace等のスコープ管理を破壊するため。 classの静的メンバ変数のconstによる定数定義を安易に使用しない。protected/privateな場合はクラス静的メンバ変数にはしない。publicな場合は、そのクラスが提供するのがふさわしい定数に限って使用する。 |
以下に、定数を使用する局面とその場合の定数定義方法を規定する。
// Foo.cpp
bool isInclude(double x, double y) {
const static double ACCELERATION_DUE_TO_GRAVITY = 9.80665;
...
}
// Foo.cpp
namespace {
const double ACCELERATION_DUE_TO_GRAVITY = 9.80665;
}
bool updatePosition(double x, double y, double tick) {
double newX = x + ACCELERATION_DUE_TO_GRAVITY * tick;
...
}
// Foo.cpp
namespace {
enum Suit {
CLUBS, DIAMONDS, HEARTS, SPADES
};
}
bool canAccept(const Suit& card) {
if (DIAMONDS == card) {
return true;
}
return false;
}
// Foo.h
namespace geophysics {
const double ACCELERATION_DUE_TO_GRAVITY = 9.80665;
}
// Bar.cpp
...
double val = geophysics::ACCELERATION_DUE_TO_GRAVITY * weight;
注記) グローバル(namespaceスコープ)のconst変数を#includeしてコンパイルされるコードを見ると、コンパイル単位でローカルな領域に定数の値が格納され、定数のシンボルは公開されないようです。(GCC/C++
4.1.2 on Linuxで確認)
// Foo.h
namespace cards {
enum Suit {
CLUBS, DIAMONDS, HEARTS, SPADES
};
}
// Bar.cpp
...
if (card.suite() == cards::SPADES) {
...
}
例2) クラス内にenum型を定義したサンプルコード
// Bar.h
class Cards {
public:
enum Suit {
CLUBS, DIAMONDS, HEARTS, SPADES
};
...
};
// Hoge.cpp
...
if (card.suite() == Cards::HEARTS) {
...
}
class Exception {};
void throwFunction {
throw Exception();
}
void doSomething() {
try {
throwFunction();
} catch (const Exception& ex) {
...
}
}