##概要
Effective C++(2項)を読んだり、他の情報を調べたりして#defineについてまとめました。
##1. カプセル化に向いていない
#defineにはprivateとかの概念が存在しない(と思っている)ので、クラス内だけである定数の値を扱いたい時などには向いていません。例えば、配列の要素数を定数を用いて宣言したい時は、
#define PLAYER_NUM 5
class GameManager {
public:
int scores[PLAYER_NUM];
};
このようになりますが、ヘッダーファイルは様々な場所から呼ばれる可能性があり"PLAYER_NUM"はどこからでも見ることが出来ますし、滅多に無いことかもしれませんがこのファイルをインクルードしたファイルで"PLAYER_NUM"という名前のマクロは重複してしまうので危険ですので、そもそもヘッダーファイルでマクロを定義するのは良くないと考えられます。
このような事態にならないようにconstを利用して定数を定義しましょう。
class GameManager {
public:
static const int PLAYER_NUM = 5;
int scores[PLAYER_NUM];
};
静的なメンバ変数に対して宣言時に値を格納できるは整数型の場合のみらしいので注意しましょう。
##2. 意図しない動作の可能性
マクロを利用した部分は定義に従ってそのまま置き換わるだけなので、使われ方によっては利用者の意図しない動作を招いてしまう場合があります。極端な例ですが、
#define CALL_WITH_MAX(a, b) f( ((a) > (b) ? (a) : (b)) )
void f(const int &x) {
std::cout << "f: value = " << x << std::endl;
}
int main(int argc, const char * argv[])
{
int a = 20;
int b = 10;
CALL_WITH_MAX(++a, b); // 2回インクリメントしてしまう
return 0;
}
f: value = 22
このような使われ方をした場合、利用者的には1回のインクリメントで良いはずですが、実際は2回インクリメントしてしまいます。このようなことにならないようにinlineを使って代わりのものを定義しましょう。
void f(const int &x) {
std::cout << "f: value = " << x << std::endl;
}
inline void callWithMax(const int &a, const int &b) {
f(a > b ? a : b);
}
int main(int argc, const char * argv[])
{
int a = 20;
int b = 10;
// CALL_WITH_MAX(++a, b);
callWithMax(++a, b); // 1回だけインクリメント
return 0;
}
結論
#defineは便利で使いやすいですが、起こる問題についてもしっかり把握した上で利用する必要があるということがわかりました。コンパイル時の制御には必須なので使わないということはこれから先なさそうですね。