C++ コーディングの作法

ここではコーディング規約/コーディング標準(coding standards/coding guidelines/coding rules)の話をしましょう
既に明文化され公表されているコーディング規約は幾つもありますし、クライアントごとプロジェクトごとに従うべきコーディング規約というものは決まっていますが、ここではプログラマとして最低限身に付けるべき作法とも言いえるような基本的なことだけ紹介してみましょう

プロジェクトのコーディング規約を優先する

ここに挙げるものは基本的なものではありますが、絶対的なものではありません
ここにあるものよりプロジェクトのコーディング規約を優先してください

また新人さんは必ずプロジェクトのコーディング規約に従ってコードを書くようにしてください
自分のスタイルと合わないところもあるでしょうけれども、勝手に無視をせず自分のスタイルよりコーディング規約を優先させなければいけません
一番やってはいけないのは、決まりを勝手に無視をすることです
どの決まりも理由があって在るものですから、決して勝手に無視をしてはいけません
この決まりは守らなくてもいいんじゃないか、と思った時にはまず先輩や上長に相談してみてください

はじめに

アプリケーションは使い手のために書く。コードは読み手のために書く。という当たり前のことが重要です
プログラムは文芸作品ではないのですから、書き手のスタイルだとかそんなことはどうでもいいのです

その上で動作への影響のある方針として重要なのは「自分よりコンパイラを信じる」「初期化を確実に」ということです

動作への影響のないところで重要なのはただ一点「書き手の意図が読み手に正しく伝わるかどうか」というところです
そこから「未来の自分は他人だと思って書く」「コードの見た目の短さより読み手の読みやすさを重視する」といった方針も出てきます

サイズ・要素数を自分で指定しない

オブジェクトのサイズや配列の要素数を何度も計算したり自分で指定したりしてはいけません
sizeof演算子や elementsofマクロなどを使ってコンパイラにやらせましょう
コンパイラは決して間違えません

×ダメ

int	foo[100];
memset( foo, 0, sizeof( int ) * 100 );

○良い

int	foo[100];
memset( foo, 0, sizeof( foo ));

サイズとは関係ないけど初期値の代入でなく初期化を使うこと

int	foo[100] = {};
//memset( foo, 0, sizeof( foo ));

×ダメ

int	foo[100];
for( int	n = 0; n < 100; ++n ){
}

○良い

int	foo[100];
for( size_t	n = 0; n < elementsof( foo ); ++n ){
}

サイズとは関係ないけど初期化すること

int	foo[100] = {};
for( size_t	n = 0; n < elementsof( foo ); ++n ){
}

オブジェクトの型を自分で指定しない

×ダメ
型を自分で指定してはダメ

foo_t	foo[] = {
	.
	.
	.
};
for( size_t	n = 0; n < sizeof( foo ) / sizeof( foo_t ); ++n ){
}

△elementsofマクロなどを使うようにした方が良い

foo_t	foo[] = {
	.
	.
	.
};
for( size_t	n = 0; n < sizeof( foo ) / sizeof( foo[0] ); ++n ){
}

○良い

foo_t	foo[] = {
	.
	.
	.
};
for( size_t	n = 0; n < elementsof( foo ); ++n ){
}

×ダメ
型を自分で指定してはダメ

foo_t	foo;
memset( &foo, 0, sizeof( foo_t ));

○良い

foo_t	foo;
memset( &foo, 0, sizeof( foo ));

○より良い
コンストラクタや初期化を使った方がより良い

foo_t	foo = {};
foo_t	foo();

同じ式を何度も書かない

同じ式が何度も出てくるならマクロにするなり関数にするなり、あるいは値をローカル変数に置くなりしてその式に適切な名前を付けましょう

×ダメ

dw1 = (wh1<<32)|wl1;
dw2 = (wh2<<32)|wl2;

○良い

#define MAKELONG(l,h)	((unsigned long)(((unsigned long)(wh1)<<32)|(unsigned long)(wl1)))
dw1 = MAKELONG(wl1,wh1);
dw2 = MAKELONG(wl2,wh2);

×ダメ

p = malloc( sizeof( wchar_t ) * ( cch + 1 ));
memset( p, 0, sizeof( wchar_t ) * ( cch + 1 ));

○良い

size_t	cb = sizeof( wchar_t ) * ( cch + 1 );
p = malloc( cb );
memset( p, 0, cb );

メンバ変数は必ず全部初期化リストで初期化する

クラスにコンストラクタを持たせるなら、必ず全ての基底クラスと全てのメンバ変数をコンストラクタの初期化リストで初期化すること
必ず!全部!初期化リストで!

×ダメ
コンストラクタでの初期値の代入ではダメ

class CFoo {
	int	m_foo;
	.
	.
	.
	CFoo(){
		m_foo = 0;
	}
};

○良い

class CFoo {
	int	m_foo;
	.
	.
	.
	CFoo():
	m_foo(0)
	{
	}
};

初期値を設定するという意味では同じでも文法上はまったく違うものです
初期化はコンストラクタが処理しますが、代入は代入演算子が処理します

初期化リストは必ず全部宣言順に並べる

初期化リストは必ず全部宣言順に並べること
全部と言ったら全部

×ダメ

class CFoo {
	int	m_foo;
	int	m_bar;
	.
	.
	.
	CFoo():
	m_bar(0),
	m_foo(0)
	{
	}
};

○良い

class CFoo {
	int	m_foo;
	int	m_bar;
	.
	.
	.
	CFoo():
	m_foo(0),
	m_bar(0)
	{
	}
};

初期化は初期化リストに書いてある順ではなく宣言順に処理されます

初期化リストで初期化しないものは必ずコメントを残す

全部と言ったら全部
だけど、パフォーマンスに影響するとか適切な理由があってどうしても初期化しないものは「必ず」コメントに残すこと

×ダメ

class CFoo {
	char	m_foo_buffer[100];
	char*	m_foo;
	.
	.
	.
	CFoo():
	m_foo(m_foo_buffer)
	{
	}
};

○良い

class CFoo {
	char	m_foo_buffer[100];
	char*	m_foo;
	.
	.
	.
	CFoo():
	//m_foo_buffer(),
	m_foo(m_foo_buffer)
	{
	}
};

×ダメ

class CFoo {
	int	m_foo;
	.
	.
	.
	CFoo(){
		m_foo = 0;
	}
};

○良い
本当はこんな意味も無いのに初期化リストで初期化しないのはダメ
ちゃんと理由のあるときは良い

class CFoo {
	int	m_foo;
	.
	.
	.
	CFoo()
	//:m_foo(0)
	{
		m_foo = 0;
	}
};

基底クラスも必ず初期化リストで明示的に初期化する

初期化リストに書いていないメンバ変数は本当に初期化されませんが、基底クラスは初期化リストに書いてあっても書いていなくても必ず初期化されます
必ず初期化はされますが、そういった動作は関係なく、必ず明示的に初期化リストに書くこと

×ダメ

class CFoo : public CBar {
	int	m_foo;
	int	m_bar;
	.
	.
	.
	CFoo():
	m_foo(0),
	m_bar(0)
	{
	}
};

×ダメ
このコメントは嘘だからダメ
コメントにしても基底クラスは必ず初期化される

class CFoo : public CBar {
	int	m_foo;
	int	m_bar;
	.
	.
	.
	CFoo():
	//CBar(),
	m_foo(0),
	m_bar(0)
	{
	}
};

○良い

class CFoo : public CBar {
	int	m_foo;
	int	m_bar;
	.
	.
	.
	CFoo():
	CBar(),
	m_foo(0),
	m_bar(0)
	{
	}
};

POD なメンバを持つ non-POD な型には必ずコンストラクタを用意する

×ダメ
params_t は POD なメンバを持つ non-POD なのにコンストラクタがない。ダメ

struct params_t {
	std::vector<int>	items;
	int	value;
};

×ダメ(バグ)
下記のように non-POD な型のインスタンスをデフォルト初期化してもその non-POD な型が持つ POD なメンバは正しく初期化されません
一見したところではゼロ初期化されそうに見えるので大変危険です

struct params_t {
	std::vector<int>	items; // このメンバは non-POD なのでユーザー定義のコンストラクタが無くても常に初期化される
	int	value; // このメンバは POD なのでユーザー定義のコンストラクタで初期化しない限り決して初期化されない!
};
class foo_t {
	int	aaa;
	params_t	params;
	.
	.
	.
	foo_t():
	aaa(),	// 初期化リストで初期値を指定せずに POD を初期化するとゼロ初期化
	params(), // コンストラクタの無い non-POD をデフォルト初期化
	.
	.
	.
	{
	}
};

○良い
POD なメンバを持つ non-POD な型には必ずコンストラクタを用意する

struct params_t {
	std::vector<int>	items;
	int	value;
	
	params_t():
	items(),
	value(0)
	{
	}
};

ローカル変数は必ず初期化する

すべてのローカル変数は必ず初期化しましょう
初期値の代入ではダメ

×ダメ

int	foo;
foo = 0;

○良い

int	foo = 0;

×ダメ

int	foo;
foo = get_foo();

○良い

int	foo = get_foo();

×ダメ

foo_struct foo;
memset( &foo, 0, sizeof( foo ));
foo.dwSize = sizeof( foo );

○良い

foo_struct foo = {};
foo.dwSize = sizeof( foo );

○良い

foo_struct foo = { sizeof( foo ) };

×ダメ

int	n;
for( n = 0; n < 100; ++n ){
}

×ダメ
無駄に初期値が違う

int	n = -1;
for( n = 0; n < 100; ++n ){
}

○良い

for( int	n = 0; n < 100; ++n ){
}

そうは言っても後で初期値を代入する配列を無駄に初期化したりするのも無駄だから、そういうのはしょうがないけど、そういうのは初期化の代わりにコメントを書くこと

×ダメ

int	foo[FOO_MAX];
for( size_t	n = 0; n < elementsof( foo ); ++n ){
	foo[n] = ...
}

×ダメ
間に余計な処理を入れたらダメ

int	foo[FOO_MAX]; // 後で初期値を代入するから初期化はしないよ
.
.
.
余計な処理
.
.
.
for( size_t	n = 0; n < elementsof( foo ); ++n ){
	foo[n] = ...
}

○良い

int	foo[FOO_MAX]; // 直後のループで初期値を代入するから初期化はしないよ
for( size_t	n = 0; n < elementsof( foo ); ++n ){
	foo[n] = ...
}

×ダメ
必ずどこかのパスで値が設定されるとしても未初期化にしてはダメ
else や default のときの値で初期化すること

int	foo;
if( bar == 0 ){
	foo = 1;
}
else if( bar == 1 ){
	foo = 7;
}
else if( bar == 2 ){
	foo = 13;
}
else{
	foo = 17;
}

○良い
(本当は数値リテラルには名前を付けなきゃダメ)

int	foo = 17;
if( bar == 0 ){
	foo = 1;
}
else if( bar == 1 ){
	foo = 7;
}
else if( bar == 2 ){
	foo = 13;
}

○良い

int	foo; // 後で初期値を代入するから初期化はしないよ
if( bar == 0 ){
	foo = 1;
}
else if( bar == 1 ){
	foo = 7;
}
else if( bar == 2 ){
	foo = 13;
}
else{
	foo = 17;
}

×ダメ

int	foo; // 後で初期値を代入するから初期化はしないよ
.
.
.
(間に余計な処理)
.
.
.
if( bar == 0 ){
	foo = 1;
}
else if( bar == 1 ){
	foo = 7;
}
else if( bar == 2 ){
	foo = 13;
}
else{
	foo = 17;
}

こういったある数値を別の数値に対応づけるような処理には名前を付けられるはずなので、関数化するとより良いでしょう

ローカル変数を使いまわさない

ローカル変数をケチって関係の無い用途に使いまわさない
いまどき別にローカル変数がいくら増えたところでどうということもないんだからローカル変数は必要なだけ用意すること

×ダメ

char*	temp = strrchr( filename, '\\' );
if( temp ){
	*temp = '\0';
}
else{
	temp = filename;
}
temp = strrchr( temp, '.' );
if( temp ){
	.
	.
	.
}

○良い

char*	pFilePart = strrchr( filename, '\\' );
if( pFilePart ){
	*pFilePart = '\0';
}
else{
	pFilePart = filename;
}
char*	pFileExt = strrchr( pFilePart, '.' );
if( pFileExt ){
	.
	.
	.
}

ローカル変数は必要になったところで宣言する

ブロックの先頭でまとめてローカル変数を宣言しない。ローカル変数は必要になったところで宣言すること
特に関係ないブロックには置かないこと

×ダメ

int	i;
int	j;
int	k;
.
.
.
for( i = 0; i < 100; ++i ){
	for( j = i+1; j < 100; ++j ){
		.
		.
		.
		k = foo(i,j);
		.
		.
		.
	}
}

○良い
本当はループ回数に数値リテラルを使うなんてのはダメ
(さらに内側のループは本当は while にすべき)

for( int	i = 0; i < 100; ++i ){
	for( int	j = i+1; j < 100; ++j ){
		.
		.
		.
		int	k = foo(i,j);
		.
		.
		.
	}
}

出力パラメータは必ず値を設定する

関数の出力パラメータは必ず値を返すこと
戻り値などの条件によって出力パラメータが設定されたり設定されなかったりということをしてはいけません

×ダメ
*baz が設定されないパスがある

int	foo( int	bar, int*	baz ){
	int	qux = get_qux( bar );
	if( qux ){
		if( baz ){
			*baz = get_baz( qux );
		}
	}
	return	qux;
}

この関数を呼び出す側は baz に触ってもいいかどうかを自分で責任を持つ必要がある

int	baz;
if( foo( bar, &baz )){
	// ここなら baz に触っても安全
}
else{
	// ここで baz は不定値
}

呼び出す側は baz を確実に初期化しておく

int	baz = invalid_baz;
foo( bar, &baz );
// baz に触っても安全

そうでなければ危険

int	baz;
foo( bar, &baz );
// ここで baz が不定値の可能性がある

○良い
呼び出し側はいつ *baz に触っても大丈夫

int	foo( int	bar, int*	baz ){
	if( baz ){
		*baz = invalid_baz;
	}
	int	qux = get_qux( bar );
	if( qux ){
		if( baz ){
			*baz = get_baz( qux );
		}
	}
	return	qux;
}

コード中に数値リテラルを使わない

このページでは数値に意味はないので例として書いているコードに数値リテラルを使っているけど、現実のコードの中に無意味なものなんて一つもないはずなので、とにかく適切に名前を付けること

×ダメ

int	users[100];

○良い
別に #define でも enum でも static const int でも何でもいい

#define MAX_USERS	100
.
.
.
int	users[MAX_USERS];

×ダメ
名前を付けたからといって要素数やサイズを再計算したりしないこと

int	users[MAX_USERS];
memset( users, 0, sizeof( int ) * MAX_USERS );

数値リテラルを自分で計算しない

コード中にいきなりわけのわからない数値リテラルが出てきても読み手は困るので、自分で勝手に計算をまとめてしまわない方がいいでしょう

×ダメ

s = d * 86400;

×ダメ
計算をまとめてしまわないのは良いのですが、数値リテラルをそのまま使ってはダメ

s = d * (24*60*60);

×ダメ
数値に名前を付けているのは良いのですが、自分で計算してはダメ

#define	SECONDS_OF_A_DAY	86400
.
.
.
s = d * SECONDS_OF_A_DAY;

○良い

#define	SECONDS_OF_A_DAY	(24*60*60)
.
.
.
seconds = days * SECONDS_OF_A_DAY;

○良い

#define	SECONDS_OF_A_MINUTE	60
#define	MINUTES_OF_AN_HOUR	60
#define	HOURS_OF_A_DAY	24
#define	SECONDS_OF_A_DAY	(HOURS_OF_A_DAY*MINUTES_OF_AN_HOUR*SECONDS_OF_A_MINUTE)
.
.
.
seconds = days * SECONDS_OF_A_DAY;

×ダメ
100 という数値に意味があるときに 100-1 を勝手に計算して 99 と書くようなことはしてはいけません

int	foo[100];
.
.
.
foo[99] = 0;

×ダメ
100 という数値に意味があるときに 100-1 を 99 ではなく 100-1 と書くことは良いのですが、しかし数値リテラルをそのまま使うのはダメですし、配列の要素数を自分で指定するのもダメです
意味があるのなら適切な名前を付けましょう

int	foo[100];
.
.
.
foo[100-1] = 0;

×ダメ
数値リテラルに名前を付けることはとても良いことですが、配列の要素数を自分で指定してはいけません

#define MAX_FOO	100
int	foo[MAX_FOO];
.
.
.
foo[MAX_FOO-1] = 0;

○良い

#define MAX_FOO	100
int	foo[MAX_FOO];
.
.
.
foo[elementsof( foo )-1] = 0;

×ダメ
例えば '0' 〜 '9' をチェックするときに勝手に自分で文字コードを解釈して下記のようにしてはいけません
-1 と 10 と 0x30 はどこから出てきたのでしょうか

int	c = cc - 0x30;
if(( c > -1 )&&( c < 10 )){
	;
}

○良い
'0' 〜 '9' に意味があるのなら '0' と '9' を使うこと
(ただし、この例の場合は本当は isdigit を使うこと)

if(( cc >= '0' )&&( cc <= '9' )){
	;
}

暗黙の演算子の優先順位を期待して括弧を省略しない

×ダメ

foo >> 1*2 + 3

○良い

foo >> ((1*2)+3)

×ダメ

foo / bar * baz

○良い

( foo / bar ) * baz

×ダメ

if( foobar == FOO || foobar == BAR ){
}

○良い

if(( foobar == FOO )||( foobar == BAR )){
}

×ダメ

if(( foobar == FOO )||( foobar == BAR )&& bar ){
}

○良い

if(( foobar == FOO )||(( foobar == BAR )&& bar )){
}

でも比較演算子の左右は別に括弧を付けなくてもいい
こんなのはダメじゃないけど冗長

if(( foo + 1 ) < FOO_MAX ){
}

○良い

if( foo + 1 < FOO_MAX ){
}

意味もないのに余計な括弧を付けない

×ダメ

char*	p = &( foo[0] );

○良い

char*	p = &foo[0];

×ダメ

char*	p = &( foo[0].bar );

○良い

char*	p = &foo[0].bar;

bool値を 0 や 1 と比較しない

×ダメ

BOOL	foo = FALSE;
.
.
.
if( foo == 0 ){
}
else{
}

△まだダメ

BOOL	foo = FALSE;
.
.
.
if( foo == FALSE ){
}
else{
}

○良い

BOOL	foo = FALSE;
.
.
.
if( !foo ){
}
else{
}

単独の == 0, != 0 は省略する

冗長だから省略した方が良い

△ダメじゃないけど冗長

if( n != 0 ){
}

○良い

if( n ){
}

△ダメじゃないけど冗長

if( n == 0 ){
}

○良い

if( !n ){
}

NULL との明示的な比較も冗長だから省略した方が良い

△ダメじゃないけど冗長

if( p == NULL ){
}

○良い

if( !p ){
}

△ダメじゃないけど冗長

if( p != NULL ){
}

○良い

if( p ){
}

例え一行であったとしても if, for, while などの後の {} を省略しない

×ダメ

BOOL	fSuccess = TRUE;
if( !p )
	fSuccess = FALSE;
if( fSuccess ){
}

○良い

BOOL	fSuccess = TRUE;
if( !p ){
	fSuccess = FALSE;
}
if( fSuccess ){
}

for は回数か範囲、whileは条件

回数か範囲を指定したループを書きたいときには for 、条件を指定したループを書きたいときには while と必ず使い分けること

×ダメ
回数でも範囲でもないなら for は使わない。条件を指定したループは while を使うこと
読み手に for は回数か範囲、while は条件と約束したら、常にそれを守る。見た目のシンプルさはどうでもいい

for(; foo ; foo = foo->get_next()){
	.
	.
	.
}

○良い

while( foo ){
	.
	.
	.
	foo = foo->get_next();
}

この例のような「リストを辿るときは for を使う」と決めるなら for でも良い。ただし必ず for を使うこと。一部は while を使わないといけないというなら for を使ってはダメ。全部を while にすること
これは要するに「指定回数繰り返す」「指定の範囲を繰り返す」「特定条件の間繰り返す」「リストを辿る」といった同じ目的には同じものを使えということ

だから単純に指定回数ループするだけのようなときには必ず for を使うこと。もし単純な指定回数のループに while を使うと、コードを読む人が for を使わない意図が何かあるのではないかと期待して読むことになるので、そういう意図が無いなら余計なことをせず素直に for を使うこと

×ダメ

int	n = 0;
while( n < 100 ){
	.
	.
	.
	++n;
}

○良い
本当はループ回数が数値リテラルなんてのはダメ

for( int	n = 0; n < 100; ++n ){
	.
	.
	.
}

○良い

for( std::vector<int>::const_iterator	p = foo.begin(); p != foo.end(); ++p ){
	.
	.
	.
}

forループのループカウンタをいじらない

ループカウンタをいじるなら for ではなく while を使うこと

×ダメ

for( int	n = 0; n < 100; ++n ){
	.
	.
	.
	if( foo( n )){
		++n;
	}
}

○良い
本当はループ回数が数値リテラルなんてのはダメ

int	n = 0;
while( n < 100 ){
	.
	.
	.
	if( foo( n )){
		++n;
	}
	++n;
}

for のループカウンタは 0→回数とする

指定回数の繰り返しのために for を使うとき、ループカウンタは「0→回数」とすること
「回数-1→0」はダメ

×ダメ

for( int	n = 100-1; n >= 0; ++n ){
	.
	.
	.
}

×ダメ(バグ)
「回数-1→0」というループではループカウンタが unsigned だと、そのままではループ終了条件の n >= 0 が常に真なので無限ループになります

for( unsigned int	n = 100-1; n >= 0; ++n ){
	.
	.
	.
}

従って、ループカウンタが unsigned のときには判定のときにループカウンタを signed にキャストするか「回数→1」のループにする必要があります

for( unsigned int	n = 100-1; ( signed int )n >= 0; ++n ){
	.
	.
	.
}
for( unsigned int	n = 100; n > 0; ++n ){
	unsigned int	m = n-1;
	.
	.
	.
}

このようにループカウンタが signed か unsigned かでループを「回数-1→0」「回数→1」などと書き方を変える必要があり、間違いの元になります
同様の問題は「0→回数」という書き方でもループ回数が INT_MAX や UINT_MAX などの場合に発生しますが、通常はループ回数がそのような回数になることはないでしょう
というわけで、for ループは必ず「0→回数」と書きましょう

○良い

for( int	n = 0; n < 100; ++n ){
	.
	.
	.
}

○良い
指定回数の逆順ループを書きたいときは下記のように書きましょう

for( int	n = 0; n < 100; ++n ){
	int	m = 100-n-1;
	.
	.
	.
}

全く最適化が無いと考えた場合「0→回数」というループよりも「回数-1→0」というループの方が若干速いかもしれません
しかし、そんなことはコンパイラにまかせましょう
パフォーマンスに影響があってどうしても「回数-1→0」にしないといけないというようなところは当然「回数-1→0」というループにすべきですが、そういう特別な場合にはコメントを書きましょう
そういった特別な場合以外は「0→回数」というループにしましょう

forループのループカウンタをループの後で使いたいようなときは while にする

×ダメ
ローカル変数を初期化してない

int	n;
for( n = 0; n < 100; ++n ){
	if( ... ){
		break;
	}
}

△ダメじゃないけど

int	n = 0;
for( n = 0; n < 100; ++n ){
	if( ... ){
		break;
	}
}

△ダメじゃないけど

int	n = 0;
for(; n < 100; ++n ){
	if( ... ){
		break;
	}
}

×ダメ
ループ回数を二重に設定している

int	m = 100;
for( int	n = 0; n < 100; ++n ){
	if( ... ){
		m = n;
		break;
	}
}

×ダメ
単純な指定回数のループじゃないし、無駄に冗長

int	m = 100;
for( int	n = 0; n < m; ++n ){
	if( ... ){
		m = n;
		break;
	}
}

○良い
小細工をせず素直に while にする

int	n = 0;
while( n < 100 ){
	if( ... ){
		break;
	}
	++n;
}

変数名は正しい英語で

間違ったスペルはダメ
日本語のローマ字表記もダメ
わからなかったら調べること

奇抜なことはしない

文法が許すからといって何をしてもいいわけではありません
テンプレートやマクロを使って奇抜なことはしないのがいいでしょう
よくできる人ほど少し書けるようになってくると boost の真似をしたくなったりするでしょうけれども、そういうことは自分用のライブラリでやりましょう
プロジェクトのコードでやるときは自分ができる知っているということよりも、プロジェクトのほかの人が理解できるかどうかを考えて書きましょう

operator&& operator|| operator, をオーバーロードしない

operator&& operator|| operator, をオーバーロードする機会はないでしょうが、絶対にオーバーロードしてはいけません。絶対

特に operator&& operator|| はオーバーロードしてしまうと、文法としての && || の動作とは異なり、条件の成否に関わらず常に両辺が評価されてしまうことになります。決してオーバーロードしてはいけません

operator, はもしかすると行列クラスの初期化のためなどにオーバーロードしたくなるかもしれません。しかし、オーバーロードしてはいけません
見た目のおもしろさよりも読み手の読み易さを優先しましょう

#endif の横に対応する #if の条件をコメントで書く

#if 〜 #endif の間はインデントしない方がいいけれど、そうすると #if 〜 #endif の対応が分かり辛くなるので、#endif の横には一目で対応する #if がわかるようにいつもコメントを書いておいた方がいい

△別にダメじゃないけど

#if	defined( __cplusplus )
.
.
.
#endif

○良い

#if	defined( __cplusplus )
.
.
.
#endif // __cplusplus

#endif の横にコメントを書くときも // を付ける

×ダメ
#endif の横は行末まで無視されるので下記のようにしても文法的には間違いではないけど、ダメ

#if	defined( __cplusplus )
.
.
.
#endif __cplusplus

○良い

#if	defined( __cplusplus )
.
.
.
#endif // __cplusplus

無限ループは while か do-while か for か、どれか一つだけをを使う

混在は読み手を混乱させます。同じ目的には同じもを使いましょう

while( true ){
	.
	.
	.
}
do{
	.
	.
	.
}while( true );
for(;;){
	.
	.
	.
}

そもそもあまり無限ループを使わないこと

インクリメント・ディクリメントは前置

特に理由の無い限りすべてのインクリメント・ディクリメントは前置を使う

×ダメ

i++;

○良い

++i;

無駄なテンポラリオブジェクトは排除する
組み込み型の場合には実際にはこの違いで無駄なコードが生成されるようなことはないのですが、普段からこういった細かいところも疎かにしないようにという意味で、徹底して前置を使うようにクセを付けた方がいいでしょう

比較演算子の左辺に定数を持ってこない

読みづらいからダメ。英語の語順に合わせて

×ダメ

if( 1 == n ){
}

○良い
本当は数値リテラルに名前を付けないとダメ

if( n == 1 ){
}

if( n = 1 ) と間違うだとかそんなことはコンパイラが警告してくれるから気にしなくていい

this は常に比較演算子の左辺に置く

読みづらいからダメ。英語の語順に合わせて

×ダメ

CFoo&	operator=( const CFoo&	rhs ){
	if( &rhs != this ){
		.
		.
		.
	}
	return	*this;
}

○良い

CFoo&	operator=( const CFoo&	rhs ){
	if( this != &rhs ){
		.
		.
		.
	}
	return	*this;
}

ハンガリアン記法は使わない

ハンガリアン記法はもうあんまり使わない方がいいかな

ただ、コードの文脈から外れるようなところにあるオブジェクトの名前には何かその印を付けておくといいでしょう

グローバルオブジェクトには g_ を付ける
public でないメンバオブジェクトには m_ を付ける
const オブジェクトなら g_, m_ ではなく c_ を付ける

○良い

CFoo	g_foo;
static CFooBar	g_foobar;

○良い

const int	c_foo[FOO_MAX] = {
	.
	.
	.
};
static const int	c_bar[BAR_MAX] = {
	.
	.
	.
};
const int	CFoo::c_baz[BAZ_MAX] = {
	.
	.
	.
};

○良い

class CFoo {
	int	m_foo;
	.
	.
	.
};

ネストしたブロック内にだけ見えるような static なオブジェクトは持たない

ネストしたブロック内にだけ見えるような static なオブジェクトは持たないこと
開発中に一時的に書くのは別にいいけど、実際に有効なコードとしてはダメ。どこかのクラスのメンバにするなりグローバルに出すなり適切な扱いをすること

×ダメ

{
	.
	.
	.
	{
		static int	s_foo = 0;
		static const char*	c_names[] = {
			.
			.
			.
		};
		if( s_foo ){
			.
			.
			.
		}
	}
	.
	.
	.
}

変数宣言などを無理に揃えない

ソースコード中では別に揃えなくていいです。ドキュメントを書きましょう

×ダメ

class CFoo {
	int								m_foo;				// コメント1
	very_very_very_very_very_very_very_very_very_very_very_long_type_name	m_very_very_long_property_name;	// コメント2
	int								m_baz;				// コメント3
	.
	.
	.
};

○別に気にしないでいい

class CFoo {
	int	m_foo;	// コメント1
	very_very_very_very_very_very_very_very_very_very_very_long_type_name	m_very_very_long_property_name;	// コメント2
	int	m_baz;	// コメント3
	.
	.
	.
};

意味も無く this-> という表記を使わない

何の意味もないのに this-> という表記を使ったりしないこと
使っていいのは必要なときだけ

×ダメ

int	CFoo::get_foo() const {
	return	this->m_foo;
}

○良い

int	CFoo::get_foo() const {
	return	m_foo;
}

○良い

int	CFoo::get_foo() const {
	return	(this->*m_callback)();
}

関数形式のマクロの引数には必ず括弧を付ける

式に置換される関数形式のマクロの引数には必ず括弧を付けるようにしましょう

×ダメ

#define elementsof( a )	(sizeof(a)/sizeof(a[0]))

○良い

#define elementsof( a )	(sizeof((a))/sizeof((a)[0]))

式に置換されるマクロは必ず全体を括弧で囲む

式に置換されるマクロは必ず全体を括弧で囲むようにしましょう

×ダメ

#define elementsof( a )	sizeof((a))/sizeof((a)[0])

○良い

#define elementsof( a )	(sizeof((a))/sizeof((a)[0]))

×ダメ

#define invalid_foo	-1

○良い

#define invalid_foo	(-1)

適切に const を付ける

参照するだけで変更しないメンバ関数やグローバルなオブジェクトや関数の引数などにはきちんと const を付けましょう
特に参照は const参照と非const参照では文法的にも扱いが異なりますので気をつけましょう
例えば「const参照は一時オブジェクトを受け取れるが、非const参照は一時オブジェクトを受け取れない」などといった違いがあります

予約されてる名前を勝手に使わない

import, export, min, max などはうっかり使ってしまいがちなので気をつけましょう

×ダメ

struct range_t {
	long	min;
	long	max;
};

×ダメ

result_t	CFoo::export( const export_params_t&	params ){
	.
	.
	.
}

基本的に C スタイルのキャストは使わない

基本的に C スタイルのキャストは使わないクセを付けた方がいいでしょう
static_cast か reinterpret_cast を使いましょう。普通はそれで事足りるはずです

dynamic_cast を使っていいかどうかはプロジェクトによって判断が分かれるので、プロジェクトの方針に従ってください

static_assert にすべきものを assert にしない

assert の条件が定数になるようなものは static_assert にできます
static_assert を使えばコンパイル時にコンパイルエラーとして検出できます

×ダメ

DYNAMIC_ASSERT( !( sizeof( foo ) % sizeof( DWORD )), "あれ、foo のサイズがおかしいよ" );

○良い

STATIC_ASSERT( !( sizeof( foo ) % sizeof( DWORD )), "あれ、foo のサイズがおかしいよ" );

標準の正式な static_assert はまだサポートされていませんが、テンプレートや要素数 0 の配列宣言などを利用して似た機能のマクロは書けます
Visual C++malloc.h にあるもの

#define _STATIC_ASSERT(expr) typedef char __static_assert_t[ (expr) ]

boost の static_assert.hpp にあるもの

template <bool x> struct STATIC_ASSERTION_FAILURE;
template <> struct STATIC_ASSERTION_FAILURE<true> { enum { value = 1 }; };
template<int x> struct static_assert_test{};
#define BOOST_STATIC_ASSERT( B )	typedef static_assert_test<sizeof(STATIC_ASSERTION_FAILURE<(bool)(B)>)>	static_assert_t

assert の条件を明示的に常に false で使うような場合には専用のマクロなどを用意する

assert の条件を明示的に常に false で使うような場合には専用のマクロなどを用意すること
例えばこんな感じで

×ダメ

if( ... ){
	DYNAMIC_ASSERT( FALSE, "万一ここに来ちゃったら警告を出すよ" );
}

○良い

#define DYNAMIC_ASSERT_ALWAYS( message_text ) \
__pragma( warning( push ))\
__pragma( warning( disable : 4127 ))\
DYNAMIC_ASSERT( FALSE, message_text )\
__pragma( warning( pop ))
.
.
.
if( ... ){
	DYNAMIC_ASSERT_ALWAYS( "万一ここに来ちゃったら警告を出すよ" );
}

non-POD な型を memset, memcpy に渡さない

仮想関数などが無く、実際には問題にならないとしても、ダメです
実際に問題になるかどうかは別にして、規格上 memset や memcpy に渡していいのは POD だけです
特に初期化が面倒だからといってコンストラクタでや代入演算子などで this を memset や memcpy に渡すようなことは、実際に問題にならないとしても、してはいけません

×ダメ

class CFoo {
	.
	.
	.
	CFoo(){
		memset( this, 0, sizeof(*this)); 
	}
	CFoo( const CFoo&	rhs ){
		memcpy( this, &rhs, sizeof( *this ));
	}
	CFoo&	operator=( const CFoo&	rhs ){
		if( this != &rhs ){
			memcpy( this, &rhs, sizeof( *this ));
		}
		return	*this;
	}
	bool	operator==( const CFoo&	rhs ){
		return	(( this == &rhs )|| !memcmp( this, &rhs, sizeof( *this )));
	}
};

delete, delete[] を直接呼ばない

メモリ管理など低レベルなコード以外では、delete, delete を直接呼ばない方がいいでしょう
例えば delete, delete
に渡されているオブジェクトの型を知りたい、というようなときにも delete, delete を直接呼ばずに下記のようなラッパ関数だけを使うようにしてあれば、とても簡単に実現できますが、delete, delete を直接呼んでいるとこのような対応が難しいためです

template<class T> void	my_delete( T&	p ){
	delete p;
	p = NULL;
}
template<class T> void	my_delete_array( T&	p ){
	delete [] p;
	p = NULL;
}
template<> void	my_delete<void*>( void*&	p );
template<> void	my_delete_array<void*>( void*&	p );

template<class T> void	my_delete( T&	p ){
	// ここに何かを仕込むのなんて簡単
	delete p;
	p = NULL;
}
template<class T> void	my_delete_array( T&	p ){
	// ここに何かを仕込むのなんて簡単
	delete [] p;
	p = NULL;
}
template<> void	my_delete<void*>( void*&	p );
template<> void	my_delete_array<void*>( void*&	p );

delete, delete[] は直接呼ばずラッパを用意してそれを使うようにしましょう

浮動小数点数の計算結果を特定の数値と == != で比較しない

例えば FLT_MIN や FLT_MAX などを利用して意味の無い値であることを確認するために == != を使うのは構いませんが、計算結果を 0.0f と比較したりしないこと
浮動小数点数の計算には常に誤差があるという前提でコードを書くべきです

△可

float	min_value = FLT_MAX;
for( size_t	n = 0; n < elementsof( foo ); ++n ){
	if( foo[n] < min_value ){
		min_value = foo[n];
	}
}
if( min_value == FLT_MAX ){
	...
}

×ダメ

float	distance = sample_calc_distance( pt1, pt2 );
if( distance == 0.0f ){
}

○良い

float	distance = sample_calc_distance( pt1, pt2 );
if( sample_fabs( distance ) <= FLT_EPSILON ){
}

FLT_MIN や FLT_MAX などを特別な意味の値として使うとしても、そのまま使わずに適切な名前を付けて使うこと

×ダメ
適切な名前を付けること

void	CFoo::foo( int	bar, float	baz ){
	if( baz == FLT_MIN ){
		baz = get_default_value( bar );
	}
	.
	.
	.
}

○良い

#define	FOO_BAZ_USEDEFAULT		FLT_MIN
.
.
.
void	CFoo::foo( int	bar, float	baz ){
	if( baz == FOO_BAZ_USEDEFAULT ){
		baz = get_default_value( bar );
	}
	.
	.
	.
}

浮動小数点数は値を浮動小数点数用のレジスタに置くか置かないかといった微妙なことでも結果が異なる場合があるので、本当に気をつけること
例えば、VC6 とかだと ( cos(1.0) == cos(1.0)) が false になるときもある

浮動小数点数をキャストで安易に整数化しない

整数になるはずの計算でも、誤差があるために整数の境界に僅かに届かない場合があります

×ダメ

int	_5 = ( int )( 2.0 / 0.4 );

△んー

int	_5 = ( int )(( 2.0 / 0.4 ) * ( 1.0 + DBL_EPSILON ));

○良い
専用の整数化機能を使う

int	_5 = sample_ftoi( 2.0 / 0.4 );

goto の作法

「goto を使うな!」というのはよく言われることではありますが、必ずしも一切使ってはいけないということではありません
しかし同時に「少しなら goto も使ってもいい」とか「文法にあるんだからどんな使い方でも気にせず goto を使っていい」というものでもありません

goto は使っても構いませんが、goto には使う上で守るべきマナーというものが幾つかあります

ブロックの出口は唯一つに

goto を使うなと言われる大きな理由の一つにブロックの出口がたくさんできてしまうという問題があります
しかも goto の場合にはその飛び先もたくさんできてしまいます
return, break, continue が goto に比べていくらかマシだというのは return, break, continue ではブロックの出口が増えることはあってもその飛び先は常に唯一つに決まっているからです

なぜブロックの出口をいくつも作ってはいけないのか

その答えはコードの構造化された状態を崩すからです
構造化されていないコードの何がいけないのかと言えばこれは簡単なことで、ただプログラマがそんなにたくさんの状態を把握しきれないからというだけのことです
つまりプログラマがコードを読み難くなるから goto を使うなということです

ですからコードが読み難くならないのであれば、あるいは返ってコードが読み易くなるのであれば、goto を使っても全く構わないわけです

上方への goto は禁止

goto を使ってもいいのはその飛び先がコードの下方にある場合だけに限定されるべきです

上方への goto でループを構成するようなコードは決して書いてはいけません

×ダメ
こんなのは絶対にダメ!

FOO_CHECK:;
	if( foo()){
		.
		.
		.
		if( bar()){
			goto FOO_CHECK;
		}
 		.
		.
		.
	}

ループを書きたければ for や while を使うべきです
「for や while では実現できないループだから仕方が無い」というようなことは決してありません
必ず goto を使わなくても済む方法があります

別ブロックへの goto は禁止

goto の飛び先が下方であっても他所のブロックに飛び込むようなことは決してしてはいけません

×ダメ
こんなのはダメ。絶対

if( foo()){
	.
	.
	.
	if( bar()){
		goto QUX_BLOCK;
	}
	.
	.
	.
	if( baz()){
		.
		.
		.
		if( qux()){
QUX_BLOCK:;
			.
			.
			.
		}
		else{
			.
			.
			.
		}
	}
}

特に if 節から else 節などに goto でジャンプするなんていうのは言語道断です

×ダメ
こんなのも絶対にダメ!

if( foo()){
	.
	.
	.
	if( bar()){
		goto ELSE_BLOCK;
	}
	.
	.
	.
}
else{
ELSE_BLOCK:;
	.
	.
	.
}

このような goto を使いたいという場合には関数を分割できないかどうか考えるべきでしょう

goto での case 間移動は禁止

switch-case でこのように goto を使って case間を移動するようなことはすべきではありません

×ダメ
ループを使え

switch( foo ){
case	FOO:
CASE_FOO:;
	{
		.
		.
		.
		if( bar())
			goto CASE_BAR;
		.
		.
		.
	}
	break;
case	BAR:
CASE_BAR:;
	{
		.
		.
		.
		if( baz())
			goto CASE_BAZ;
		.
		.
		.
	}
	break;
case	BAZ:
CASE_BAZ:;
	{
		.
		.
		.
		if( foo())
			goto CASE_FOO;
		.
		.
		.
	}
	break;
}

case 間を移動したい場合にはループを使うべきです
またこのような goto を使いたいという場合には関数を分割することも考えるべきでしょう

多重ループからの脱出のための goto は可

多重ループからの脱出のために goto を使うことは比較的問題が少ないと言えるでしょう

△可

for( int	y = 0; y < cy; ++y ){
	for( int	x = 0; x < cx; ++x ){
		if( foo()){
			goto end_of_this_loop;
		}
		.
		.
		.
	}
}
end_of_this_loop:;

ただし、このような goto を使う場合このループには break や continue などを使うべきではありません

ブロック冒頭での検査のための goto は可

関数の先頭などブロック冒頭での検査のために goto を使うことは比較的問題が少ないと言えるでしょう

△可

FOO*	get_foo(){
	FOO*	pFoo = NULL;
	if( !m_bar ){
		goto	end_of_this_function;
	}
	.
	.
	.
end_of_this_function:;
	return	pFoo;
}

ただし、このような goto を使う場合この関数では関数途中での return は使うべきではありませんし、goto で抜けるのはそのブロックだけにした方が望ましいでしょう

case の最後で break しないときは必ずコメントを書く

case の最後で break しないときは必ずコメントを書きましょう

×ダメ

switch( mode ){
case	FOO:
	mode = BAR;
case	BAR:
.
.
.
}

○良い

switch( mode ){
case	FOO:
	mode = BAR;
	// break しない
case	BAR:
.
.
.
}

ただ「break しない」というだけでなく「○○だから break しない」というようにその理由も書いておくとより良いと思います

ただし、コメントを書けばいいというものでもありません
下記のように条件によって break したりしなかったりといったことはやめましょう

×ダメ

switch( foo ){
case	FOO:
	.
	.
	.
	if( !bar()){
		break;
	}
	// break しないよ
case	BAR:
	.
	.
	.
	break;
}

このような場合には各case を関数に分割できないかどうか考えるべきでしょう

continue はなるべく使わない

continue はなるべく使わない方がいいでしょう
使うにしても次に挙げるようなところには特に気をつけましょう

無駄に continue しない

例えば 0 でない要素の数を数えるのにこんなループは書かない

×ダメ

size_t	n = 0;
for( std::vector<int>::iterator	p = a.begin(); p != a.end(); ++p ){
	if( !*p ){
		continue;
	}
	++n;
}

○良い

size_t	n = 0;
for( std::vector<int>::iterator	p = a.begin(); p != a.end(); ++p ){
	if( *p ){
		++n;
	}
}

continue を使わないとネストが深くなりすぎたりといったことがあるなら関数に分割できないかどうか検討すべきです

switch-case の中で continue しない

×ダメ

while( get_msg( &msg )){
	switch( msg.message ){
	case	FOO:
		if( foo_cancelled( &msg )){
			continue;
		}
		.
		.
		.
		break;
	}
}

break すべきところで continue しない

例えば最初の 0 でない値を得るのに普通はこんなループは書かない

×ダメ

int	value = 0;
for( std::vector<int>::iterator	p = a.begin(); p != a.end(); ++p ){
	if( !*p || value ){
		continue;
	}
	value = *p;
}

○良い

int	value = 0;
for( std::vector<int>::iterator	p = a.begin(); p != a.end(); ++p ){
	if( *p ){
		value = *p;
		break;
	}
}

このコードを while で書き換えると下記のようなコードになりますが
下記のコードでは continue するときに ++p と次のループのために continue しないときと同じ処理をしています
こんなのは特にダメです

×ダメ

int	value = 0;
std::vector<int>::iterator	p = a.begin();
while( p != a.end()){
	if( !*p || value ){
		++p;
		continue;
	}
	value = *p;
	++p;
}

ループの中に無条件な break や continue を書かない

例えば最初の 0 でない値を得るのに普通はこんなループは書かない

×ダメ

int	value = 0;
for( std::vector<int>::iterator	p = a.begin(); p != a.end(); ++p ){
	if( !*p ){
		continue;
	}
	value = *p;
	break;
}

○良い
同じ意味なのですからわざわざ変な書き方をするのはやめてこのように普通に書きましょう

int	value = 0;
for( std::vector<int>::iterator	p = a.begin(); p != a.end(); ++p ){
	if( *p ){
		value = *p;
		break;
	}
}

巨大な switch-case を書かない

同じ関数であっても呼び出されたときの内部的な状態に応じて全く異なる動作をする関数、そしてまた同じ関数であっても呼び出されたときに外部から指定されるパラメータに応じて全く異なる動作をする関数、このような関数が使われるのは例えば予め登録しておいたコールバック関数が様々な状態に応じて呼び出されるといったような場合が当てはまると思います
具体的には Windows のウィンドウプロシージャがそうですね

Fig.1

LRESULT	WindowProc_Sample( HWND	hwnd, UINT	uMsg, WPARAM	wParam, LPARAM	lParam ){
	LRESULT	lresult = 0;
	switch( uMsg ){
	case	WM_CREATE:
		{
			LPCREATESTRUCT	pcs = ( LPCREATESTRUCT )lParam;
			...
		}
		break;
	case	WM_DESTROY:
		...
		break;
	case	WM_COMMAND:
		{
			UINT	wNotifyCode = ( UINT )HIWORD(wParam);
			UINT	wID = ( UINT )LOWORD(wParam);
			HWND	hwndCtl = ( HWND )lParam;
			...
		}
		break;
	case	WM_LBUTTONDOWN:
		{
			UINT	fwKeys = ( UINT )wParam;
			POINT	pt = { ( LONG )( SHORT )LOWORD(lParam), ( LONG )( SHORT )HIWORD(lParam) };
			...
		}
		break;
	.
	.
	.
	}
	return	lresult;
}

このような関数を実装するときに Fig.1 のように各 case に直に処理を書いてしまって結果として関数の中身は巨大な switch-case が1つだけというようなコードになってしまっている人がいます
こんなのはダメです
なぜダメなのでしょう

考えてみてください
この switch-case の個々の case は本来はそれぞれ独立した関数であるべきものがインターフェイスの仕様の都合によって仕方なく1つの関数にまとめられているだけではないですか?
例えば Fig.1 では uMsg が WM_CREATE のときにはその WM_CREATE の case の部分だけが処理されればよくて他の case の部分は全く必要のないものですよね
また uMsg が WM_DESTROY のときにはその WM_DESTROY の case の部分だけが処理されればよくて他の case の部分は全く必要のないものですし、他の case も同じですね
それぞれの case は全く関係のないコードなのですから、それなら本来は別々の関数に分けるべきなのです
Fig.2

LRESULT	OnCreate( HWND	hwnd, UINT	uMsg, WPARAM	wParam, LPARAM	lParam ){
	...
}
LRESULT	OnDestroy( HWND	hwnd, UINT	uMsg, WPARAM	wParam, LPARAM	lParam ){
	...
}
LRESULT	OnCommand( HWND	hwnd, UINT	uMsg, WPARAM	wParam, LPARAM	lParam ){
	...
}
LRESULT	OnLButtonDown( HWND	hwnd, UINT	uMsg, WPARAM	wParam, LPARAM	lParam ){
	...
}

そして巨大な switch-case があった元の関数は単純に振り分ける処理だけに専念するようにします
Fig.3

LRESULT	WindowProc_Sample( HWND	hwnd, UINT	uMsg, WPARAM	wParam, LPARAM	lParam ){
	LRESULT	lresult = 0;
	switch( uMsg ){
	case	WM_CREATE:
		lresult = OnCreate( hwnd, uMsg, wParam, lParam );
		break;
	case	WM_DESTROY:
		lresult = OnDestroy( hwnd, uMsg, wParam, lParam );
		break;
	case	WM_COMMAND:
		lresult = OnCommand( hwnd, uMsg, wParam, lParam );
		break;
	case	WM_LBUTTONDOWN:
		lresult = OnLButtonDown( hwnd, uMsg, wParam, lParam );
		break;
	.
	.
	.
	}
	return	lresult;
}

これを面倒くさがるから巨大な switch-case ができてしまうのです

もちろんこれはウィンドウプロシージャのように外部から指定されたパラメータによって処理を振り分ける場合だけでなく内部的な状態変数によって処理を振り分けるような場合も同様です
Fig.4

void	CSample::DoSomething(){
	switch( m_mode ){
	case	SAMPLE_MODE_AAA:
		...
		break;
	case	SAMPLE_MODE_BBB:
		...
		break;
	case	SAMPLE_MODE_CCC:
		...
		break;
	case	SAMPLE_MODE_DDD:
		...
		break;
	.
	.
	.
	}
}

こんな場合も次のように振り分け部分と実際の処理部分とに必ず分けるようにしましょう
Fig.5

void	CSample::OnAAA(){
	...
}
void	CSample::OnBBB(){
	...
}
void	CSample::OnCCC(){
	...
}
void	CSample::OnDDD(){
	...
}

Fig.6

void	CSample::DoSomething(){
	switch( m_mode ){
	case	SAMPLE_MODE_AAA:
		OnAAA();
		break;
	case	SAMPLE_MODE_BBB:
		OnBBB();
		break;
	case	SAMPLE_MODE_CCC:
		OnCCC();
		break;
	case	SAMPLE_MODE_DDD:
		OnDDD();
		break;
	.
	.
	.
	}
}

これを面倒くさがらないでください
面倒くさいわけではなく「ある case の後に続けて別の case の処理を実行したいから分割できない」とか「状況に応じて case 間をいろいろに移動するので分割できない」と言う人もいるかもしれませんが、しかし、そのような場合には処理を振り分ける関数内で適切なループを書きましょう
Fig.7

void	CSample::DoSomething(){
	int	mode;
	do{
		mode = m_mode;
		switch( m_mode ){
		case	SAMPLE_MODE_AAA:
			OnAAA();
			break;
		case	SAMPLE_MODE_BBB:
			OnBBB();
			break;
		case	SAMPLE_MODE_CCC:
			OnCCC();
			break;
		case	SAMPLE_MODE_DDD:
			OnDDD();
			break;
		.
		.
		.
		}
	}while( mode != m_mode );
}

あるいはこのようにしてもいいでしょう
Fig.8

BOOL	CSample::DoSomething_Dispatch(){
	BOOL	fDone = TRUE;
	switch( m_mode ){
	case	SAMPLE_MODE_AAA:
		fDone = OnAAA();
		break;
	case	SAMPLE_MODE_BBB:
		fDone = OnBBB();
		break;
	case	SAMPLE_MODE_CCC:
		fDone = OnCCC();
		break;
	case	SAMPLE_MODE_DDD:
		fDone = OnDDD();
		break;
	.
	.
	.
	}
	return	fDone;
}
void	CSample::DoSomething(){
	while( !DoSomething_Dispatch()){
		;
	}
}

あるいはまた例えば OnAAA から OnBBB を呼び出すなどの方法でも対処できるでしょう
いずれにせよ巨大な switch-case を書く言い訳にはなりません

さて、ここまでは switch-case を例に使ってきましたが、このようなことはもちろん switch-case という構文だけに限ったことではありません
例えば次のようなコードではどうでしょう
Fig.9

void	CSample::DoSomething(){
	if( IsAAA()){
		...
		// AAA のとき固有の処理
		...
	}
	else{
		...
	}
	.
	.
	.
	// 何か共通の処理
	.
	.
	.
	if( IsAAA()){
		...
		// AAA のとき固有の処理
		...
	}
	else{
		...
	}
	.
	.
	.
	// 何か共通の処理
	.
	.
	.
	if( IsAAA()){
		...
		// AAA のとき固有の処理
		...
	}
	else{
		...
	}
	.
	.
	.
	// 何か共通の処理
	.
	.
	.
	if( IsAAA()){
		...
		// AAA のとき固有の処理
		...
	}
	else{
		...
	}
}

この CSample::DoSomething というメソッドは IsAAA が真を返すときと偽を返すときとで全く別のことをするように書かれています
しかし、このようにそれぞれの場合で全く別のことをするならそれを一緒の関数に書いておく理由は何もありません
つまりこれも上で述べてきた場合と同様に適切に分割すべき例だということですね
処理を分けるのが面倒だと言わずに分割すべきところはきちんと分割しましょう

この「処理を適切な単位に分割して関数とする」というのはプログラミングの初歩の初歩ですから面倒くさがらずに実践するように心掛けてください