マクロは他のツールとまったく同じです-殺人で使用されるハンマーはハンマーなので悪ではありません。そのように人がそれを使う方法は悪いです。釘で打ちたい場合は、ハンマーが最適です。
マクロを「悪い」ものにするいくつかの側面があります(後で詳しく説明し、代替案を提案します)。
- マクロはデバッグできません。
- マクロ展開は、奇妙な副作用を引き起こす可能性があります。
- マクロには「名前空間」がないため、他の場所で使用されている名前と競合するマクロがある場合、不要な場所でマクロが置き換えられ、通常は奇妙なエラーメッセージが表示されます。
- マクロは、気づかないことに影響を与える可能性があります。
ここで少し拡張してみましょう:
1)マクロはデバッグできません。
数値または文字列に変換されるマクロがある場合、ソースコードにはマクロ名が含まれ、多くのデバッガーでは、マクロの変換先を「見る」ことができません。だから、実際に何が起こっているのかわかりません。
交換:enum
またはを使用const T
「関数のような」マクロの場合、デバッガーは「現在のソース行ごと」レベルで機能するため、マクロは1つのステートメントであるか100であるかに関係なく、1つのステートメントのように機能します。何が起こっているのか理解するのを難しくします。
置換:関数を使用します-「高速」にする必要がある場合はインラインで使用します(ただし、インラインを多すぎるのは良くないことに注意してください)
2)マクロ展開は奇妙な副作用をもたらす可能性があります。
有名なもの#define SQUARE(x) ((x) * (x))
とその使い方x2 = SQUARE(x++)
。これはにつながりますx2 = (x++) * (x++);
。これは、たとえそれが有効なコード[1]であったとしても、ほぼ間違いなくプログラマーが望んでいたものではありません。関数の場合、x ++を実行しても問題はなく、xは1度だけインクリメントします。
もう1つの例は、マクロの「if else」です。これがあるとしましょう。
#define safe_divide(res, x, y) if (y != 0) res = x/y;
その後
if (something) safe_divide(b, a, x);
else printf("Something is not set...");
それは実際には完全に間違ったものになります...
交換:実際の機能。
3)マクロには名前空間がありません
マクロがある場合:
#define begin() x = 0
そしてbeginを使用するC ++のコードがあります。
std::vector<int> v;
... stuff is loaded into v ...
for (std::vector<int>::iterator it = myvector.begin() ; it != myvector.end(); ++it)
std::cout << ' ' << *it;
ここで、どのエラーメッセージが表示されると思いますか。どこでエラーを探しますか(他の誰かが書いたヘッダーファイルにあるbeginマクロを完全に忘れてしまった、または知らなかった場合)。[そして、インクルードの前にそのマクロをインクルードした場合はさらにおもしろい-コード自体を見てもまったく意味のない奇妙なエラーに溺れてしまうでしょう。
置換:まあ、「ルール」ほど置換はありません-マクロには大文字の名前のみを使用し、他のものにはすべて大文字の名前を使用しないでください。
4)マクロには気づかない効果がある
この関数を実行します。
#define begin() x = 0
#define end() x = 17
... a few thousand lines of stuff here ...
void dostuff()
{
int x = 7;
begin();
... more code using x ...
printf("x=%d\n", x);
end();
}
さて、マクロを見ないで、beginはxに影響を与えてはならない関数であると考えるでしょう。
この種のこと、そしてもっと複雑な例を見てきましたが、本当にあなたの1日を台無しにする可能性があります!
置換:マクロを使用してxを設定しないか、xを引数として渡します。
マクロの使用が明らかに有益な場合があります。1つの例は、ファイル/行情報を渡すためにマクロで関数をラップすることです:
#define malloc(x) my_debug_malloc(x, __FILE__, __LINE__)
#define free(x) my_debug_free(x, __FILE__, __LINE__)
my_debug_malloc
コードで通常のmallocとして使用できるようになりましたが、追加の引数があるため、最後に「解放されていないメモリ要素」をスキャンすると、割り当てが行われた場所を印刷できるので、プログラマはリークを追跡できます。
[1]「シーケンスポイント内」で1つの変数を複数回更新することは未定義の動作です。シーケンスポイントはステートメントと正確に同じではありませんが、ほとんどの目的と目的のために、それを検討する必要があります。したがって、これを行うx++ * x++
とx
2回更新さx
れます。これは未定義であり、おそらく異なるシステムでは異なる値になり、同様に異なる結果値になります。
#pragma
マクロではありません。