コメントが必要ないコードを書くよう努力しよう
前回のエントリは、このエントリを書くための、実は前振り。
不必要なコメントが有るより、必要なコメントが無い方が問題だと思うので、エントリを別にして、先に書いておくことにした。短いので、ぜひ先に読んでおいてほしい。
読んだ?
さて、前回の要点は、以下の三つ。
- コメントは、コードではないというところが最大の欠点。
- 不要なコメントは、ただ不要というそれだけの理由で、書いてはならない。
- 文書化されておらず、コードで表現し切れていないことは、すべて書こう。
だから、できるだけ表現豊かにコードを書こう、というのが今回の要旨。
そうすれば必要なコメントは減り、したがってコメントとコードの乖離の危険も減る。
それがなんであるかを示す名前をつけよう
#include
double calc( double x )
{
return pow( x, 2 ) * sqrt( 3 ) / 4;
}
とか書くなと。何をしてるか分かっても、これで何が得られるのか、分からないから。
#include
double getRegularTriangleArea( double sideLength )
{
return pow( sideLength, 2 ) * sqrt( 3 ) / 4;
}
こう書いておけば、引数を一辺の長さとした正三角形の面積が得られることが分かる。
ヘンに省略しない。「type」だの「mode」だの「status」だのって、なんのことか分かりませんから。
長い識別子を作っても、実行速度にも使用メモリにも影響しないので、全然かまわない。
識別子を使うときは、エディタの補完機能*1を使おう。宣言時にtypoしてもコンパイルエラーにならないので、気づかずに使い続けてしまうけど。
命名の際には、正しい英語を使う。
ウィンドウを開くなら、「windowOpen」ではなくて「openWindow」。動詞が先、目的語が後。
英語が間違っていると、意味が伝わらない。
ちなみに、Cランタイムを使わないで、C++のみで書くと以下の通り。
#include
template< typename ArgType >
ArgType getRegularTriangleArea( const ArgType & sideLength )
{
return std::pow( sideLength, ArgType( 2 ) ) * std::sqrt( ArgType( 3 ) ) / ArgType( 4 );
}
Cランタイムは、いろいろと脆弱性を抱えているので、C++のコンパイラを使っているなら、可能な限りC++を使おう。
まあ、VC8のソースを見る限り、pow()とかsqrt()は、組み込み型を渡すとCランタイムを呼ぶんだけど。
処理に名前をつけよう
なっがーい関数の中に、「ここでナニをやります」なんてコメントを見ると、「じゃあ関数に切り出しておけよ」と思う。
「処理に名前をつける」とはそういうこと。どんな名前をつけるかは、上記参照。
関数名で処理の説明をしておけば、コメントの必要が無くなる。
if文の中で、複雑な判定式を書くのなら、その式を「isVeryComplicationCondition()」みたいな名前の関数に切り出して、判定結果をreturn文に渡そう。
リテラル値を使うのはやめよう
「3.1415926535」とか「"Input file name.\n"」とかを、直接式の中に書くのはやめよう。
円周率あたりはまだ分かりやすいが、
int diskType = 1; // HDDで初期化
...
switch( diskType )
{
case 0: // FD
...
break;
case 1: // HDD
...
break;
case 2: // CD
...
break;
default:
...
break;
}
なんてのは、まったく最悪。値を使う度にコメントを書かねばならないし、ひどいとそのコメントすらない。
こういうのは、enumを使おう。
enum DISK_TYPE
{
DISK_TYPE_FD,
DISK_TYPE_HDD,
DISK_TYPE_CD,
};
DISK_TYPE discType = DISK_TYPE_HDD;
...
switch( discType )
{
case DISK_TYPE_FD:
...
break;
case DISK_TYPE_HDD:
...
break;
case DISK_TYPE_CD:
...
break;
default:
...
break;
}
リテラル値に名前をつける際に、決して#defineを使ってはならない。constを使おう。
#defineは、コンパイルの前にテキストを置換するので、C++の名前空間が食い破られてしまう。その傍若無人ぶりには、頭を抱える。
例えば、VCではmaxという#defineマクロが定義されているが、おかげでstd::numeric_limits::max()を使うことができない。
しょうがないので、こんなコードを書くのだ。
#include
#if defined(max)
#define RESERVED_MAX_MACRO max
#undef max
#endif // defined(max)
template< typename ResultType >
ResultType getMax()
{
return std::numeric_limits< ResultType >::max();
}
#if defined(RESERVED_MAX_MACRO)
#define max RESERVED_MAX_MACRO
#undef RESERVED_MAX_MACRO
#endif // defined(RESERVED_MAX_MACRO)