12

私が C++ を開発してから 1 年も経っていませんが、その間、複数の人が C++ の恐ろしさについて話しているのを聞いたこと#defineがあります。今、コンパイラではなくプリプロセッサによって解釈されているため、デバッグできないことがわかりましたが、これは本当に悪いことですか?

以下に例を示します (テストされていないコードですが、一般的な考え方は理解できます)。

#define VERSION "1.2"

#include <string>

class Foo {
  public:
    string getVersion() {return "The current version is "+VERSION;}
};
  1. なぜこのコードは悪いのでしょうか?
  2. を使用する代わりの方法はあり#defineますか?
4

5 に答える 5

14

なぜこのコードは悪いのでしょうか?

VERSION は上書きされる可能性があり、コンパイラは通知しないためです。

#define を使用する代わりの方法はありますか?

const char * VERSION = "1.2";

また

const std::string VERSION = "1.2";
于 2012-04-21T18:40:43.163 に答える
11

本当の問題は、定義が言語の残りの部分 (プリプロセッサ) とは異なるツールによって処理されることです。結果として、コンパイラはそれを認識せず、プリプロセッサ名の再利用など、何か問題が発生した場合にユーザーを助けることができません。

maxマクロとして実装されることがあるケースを考えてみましょう。結果として、maxコード内のどこでも識別子を使用することはできません。どこでも。しかし、コンパイラは教えてくれません。代わりに、コードがひどく間違ってしまい、その理由がわかりません。

さて、この問題は、注意を払って最小限に抑えることができます (完全に排除されていない場合)。しかし、ほとんどの用途で#defineは、より良い代替手段があるため、費用対効果の計算が歪められます。メリットがまったくないのにわずかなデメリットがあります。利点がないのに、なぜ欠陥のある機能を使用するのでしょうか?

したがって、ここに非常に単純な図があります。

  1. 定数が必要ですか?定数を使用する (定義ではない)
  2. 関数が必要ですか?関数を使用する (定義ではない)
  3. 定数や関数を使用してモデル化できないものが必要ですか? 定義を使用しますが、適切に行ってください。

それを「適切に」行うこと自体が芸術ですが、簡単なガイドラインがいくつかあります。

  1. 固有の名前を使用してください。すべて大文字で、常に一意のライブラリ識別子が前に付きます。max? 外。VERSION? 外。代わりに、 と を使用MY_COOL_LIBRARY_MAXMY_COOL_LIBRARY_VERSIONます。たとえば、Boost ライブラリ、マクロの大量のユーザーは、常に で始まるマクロを使用しますBOOST_<LIBRARY_NAME>_

  2. 評価に注意。実際には、マクロ内のパラメーターは置き換えられる単なるテキストです。結果として、#define MY_LIB_MULTIPLY(x) x * xは壊れています: として使用できMY_LIB_MULTIPLY(2 + 5)、結果として2 + 5 * 2 + 5. 私たちが望んでいたものではありません。これを防ぐために、常に引数のすべての使用を括弧で囲みます (自分が何をしているのか正確にわかっている場合を除きます – ネタバレ: おそらく知らないでしょう。専門家でさえ驚くほど頻繁にこれを間違えます)。

    このマクロの正しいバージョンは次のようになります。

     #define MY_LIB_MULTIPLY(x) ((x) * (x))
    

しかし、マクロをひどく間違ったものにする方法はまだたくさんあります。繰り返しますが、コンパイラはここでは役に立ちません。

于 2012-04-21T18:50:05.433 に答える
4

#define本質的に悪いわけではなく、悪用しやすいだけです。バージョン文字列のようなものでは問題なく動作しますが、a のconst char*方が優れていますが、多くのプログラマーはそれ以上の目的で使用しています。#defineたとえば、ほとんどの場合、typedef の方が優れている場合、typedef として使用するのはばかげています。したがって、ステートメントに問題はなく、#defineステートメントがないとできないこともあります。ケースバイケースで評価する必要があります。プリプロセッサを使用せずに問題を解決する方法を見つけられる場合は、それを実行する必要があります。

于 2012-04-21T18:40:44.240 に答える
2

#define定数使用staticキーワード を定義するために使用することはありませconst int kMajorVer = 1; const int kMinorVer = 2; ん 。const std::string kVersion = "1.2";

Herb sutter の優れた記事で、なぜ#defineが悪いのかを詳しく説明し、同じことを実現する方法が他にない例をいくつか挙げています: http://www.gotw.ca/gotw/032.htm .

基本的には、多くの場合、正しく使用すれば問題ありませんが、悪用しやすく、マクロ エラーは特に不可解であり、バグをデバッグするのに役立ちます。

私は個人的にそれらを条件付きデバッグ コードとバリアント データ表現にも使用します。これについては、sutter 記事の最後で詳しく説明します。

于 2012-04-21T19:00:23.060 に答える
1

一般に、プリプロセッサは安全ではない 2 パス コンパイル プロセスを作成し、エラー メッセージをデコードするのが難しく、読みにくいコードにつながる可能性があるため、不適切です。可能であれば使用しないでください。

const char* VERSION = "1.2"

ただし、プリプロセッサなしではやりたいことを実行できない場合があります。

#define Log(x) cout << #x << " = " << (x) << endl;
于 2012-04-21T18:44:35.777 に答える