LoginSignup
15
15

More than 5 years have passed since last update.

不用意な#defineの使用は出来る限り避ける

Last updated at Posted at 2014-07-28

概要

Effective C++(2項)を読んだり、他の情報を調べたりして#defineについてまとめました。

1. カプセル化に向いていない

#defineにはprivateとかの概念が存在しない(と思っている)ので、クラス内だけである定数の値を扱いたい時などには向いていません。例えば、配列の要素数を定数を用いて宣言したい時は、

#define PLAYER_NUM 5

class GameManager {
public:
    int scores[PLAYER_NUM];
};

このようになりますが、ヘッダーファイルは様々な場所から呼ばれる可能性があり"PLAYER_NUM"はどこからでも見ることが出来ますし、滅多に無いことかもしれませんがこのファイルをインクルードしたファイルで"PLAYER_NUM"という名前のマクロは重複してしまうので危険ですので、そもそもヘッダーファイルでマクロを定義するのは良くないと考えられます。

このような事態にならないようにconstを利用して定数を定義しましょう。

GameManager.h
class GameManager {
public:
    static const int PLAYER_NUM = 5;
    int scores[PLAYER_NUM];
};

静的なメンバ変数に対して宣言時に値を格納できるは整数型の場合のみらしいので注意しましょう。

2. 意図しない動作の可能性

マクロを利用した部分は定義に従ってそのまま置き換わるだけなので、使われ方によっては利用者の意図しない動作を招いてしまう場合があります。極端な例ですが、

main
#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;
}
Result
f: value = 22

このような使われ方をした場合、利用者的には1回のインクリメントで良いはずですが、実際は2回インクリメントしてしまいます。このようなことにならないようにinlineを使って代わりのものを定義しましょう。

main
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は便利で使いやすいですが、起こる問題についてもしっかり把握した上で利用する必要があるということがわかりました。コンパイル時の制御には必須なので使わないということはこれから先なさそうですね。

15
15
7

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
15