web-dev-qa-db-ja.com

C ++のマクロとconstの違いは何ですか?

私は技術面接でこの質問をされました:

constとC++のマクロの違いは何ですか?

私の答えは、マクロはプリプロセッサディレクティブであり、コンパイル前に定数式に置き換えられるため、マクロを使用するとアプリケーションのデバッグが困難になる可能性があるのに対し、constは型識別子を持つことができ、デバッグが簡単です。

誰かが他の違いを指摘できますか?どちらが好ましいですか?

編集:

C++のIBM資料から:

#defineconst型修飾子の違いは次のとおりです。

  1. #defineディレクティブを使用して、数値、文字、または文字列定数の名前を作成できますが、任意のタイプのconstオブジェクトを宣言できます。

  2. Constオブジェクトは変数のスコープ規則に従いますが、#defineを使用して作成された定数はそうではありません。 constオブジェクトとは異なり、マクロの値はインライン展開されるため、コンパイラーが使用する中間ソースコードには表示されません。インライン展開により、デバッガーはマクロ値を使用できなくなります。

  3. マクロは、配列バインドなどの定数式で使用できますが、constオブジェクトは使用できません。 (array_sizeを定義するにはマクロを使用する必要があると思います。

  4. コンパイラは、マクロ引数を含むマクロの型チェックを行いません。

21
Amm Sokun

マクロと定数はリモートで同じものではなく、それぞれが状況に適している場合があり、あなたの答えは違いの表面をかすっただけです。また、C++には2種類の定数があります。

const修飾子で定義された定数は、変更不可能変数として最もよく考えられます。変数のすべてのプロパティがあります。タイプ、サイズ、リンケージがあり、アドレスを取得できます。 (コンパイラーは、これらのプロパティの一部を最適化して回避できる場合があります。たとえば、アドレスが使用されない定数は実行可能イメージに出力されない場合があります。ただし、これはas-ifルールのおかげです。 )constデータムに対して実行できない唯一のことは、その値を変更することです。 enumで定義された定数は少し異なります。タイプとサイズはありますが、リンケージがなく、アドレスを取得できず、タイプは一意です。これらは両方とも変換フェーズ7で処理されるため、左辺値または右辺値以外の何物でもありません。 (前の文の専門用語については申し訳ありませんが、それ以外の場合はいくつかの段落を書く必要があります。)

マクロの制約ははるかに少なく、プログラム全体が整形式のプログラムである限り、トークンの任意のシーケンスに拡張できます。変数のプロパティはありません。 sizeofまたは&をマクロに適用すると、マクロの展開先に応じて、何か便利なことができる場合とできない場合があります。マクロは、数値リテラルに展開するように定義される場合があり、そのようなマクロは定数として考えられる場合がありますが、「コンパイラー固有」(つまり、変換フェーズ7)は、それらを数値リテラルと見なします。

今日では、定数を使用するときにマクロを使用しないことは、一般的に良い習慣と考えられています。マクロは他のすべての識別子と同じスコープ規則に従わないため、混乱する可能性があります。定数を使用すると、変換フェーズ7に、したがってデバッガーにも詳細情報が提供されます。ただし、マクロを使用すると、他の方法では実行できないことを実行できます。これらのいずれかを実行する必要がある場合は、遠慮なく使用してください。 (この意味で、重みを引いているマクロは、一般的にnotを数値リテラルに展開しますが、決して言うつもりはありません。)

EDIT:これは何か面白いことをするマクロの例です。それは決して定数を形作ったり形作ったりするものではありません。マクロなしで同じ効果を得る方法があるかもしれませんが(文字列ストリームを含まないものを知っているなら、それについて聞いてみたいです!)、それは力と両方の良い例証になると思いますマクロの危険性(後者の場合、1つの非常に特定のコンテキストの外で使用された場合にどうなるかを検討してください...)

static double elapsed()
{ ... }
#define ELAPSED '[' << std::fixed << std::setprecision(2) << elapsed() << "] "

// usage:
for (vector<string>::iterator f = files.begin(); f != files.end(); f++) {
    cout << ELAPSED << "reading file: " << *f << '\n';
    process_file(*f);
}
25
zwol

いくつかの理由から、const int sum = 1;よりも#define sum 1を優先する必要があります。

スコープベースのメカニズム:

#definesはスコープを尊重しないため、クラススコープの名前空間を作成する方法はありません。 const変数は、クラス内でスコープを設定できます。

コンパイルエラー中の奇妙なマジックナンバーの回避:

#defineを使用している場合、プリコンパイル時にプリプロセッサに置き換えられます。したがって、コンパイル中にエラーが発生した場合、エラーメッセージはマクロ名ではなく値を参照し、表示されるため、混乱を招きます。突然の値であり、コードでそれを追跡するのに多くの時間を浪費するでしょう。

デバッグのしやすさ:

また、同じ理由で、#defineをデバッグしても、実際には役に立ちません。
上記の両方の状況を回避するには、constの方が適しています。

12
Alok Save

もう1つの違いは、const変数にはメモリがあり、ポインタで参照できることです。マクロはコンパイル前に発生するオートコンプリートであるため、コンパイル中に名前が失われます。

また、マクロは定数以上のものにすることができます。関数の定義全体であっても、式または構文的に正しいものであれば何でもかまいません。

マクロは、プログラミングの選択を表すために使用されます。スタックサイズ;一方、cosntは、Piやeの値などの実世界の定数を表すために使用されます。

2
Xolve

(元々投稿された static const vs #define -この質問はより「勢い」があるように思われるのでここで再現...それが不適切かどうか教えてください...)

使用法に応じて、すべての長所と短所:

  • consts
    • 適切にスコープされた/識別子の衝突の問題はうまく処理されました
    • 強力な単一のユーザー指定タイプ
      • _#define_ ala #define S std::string("abc")を「入力」しようとするかもしれませんが、定数は、使用の各ポイントで別個の一時的なものを繰り返し構築することを回避します。
    • 1つの定義ルールの複雑さ
    • アドレスを取得したり、それらへのconst参照を作成したりできます。
  • 定義
    • 「グローバル」スコープ/使用法が競合する傾向があり、正常なエラーメッセージではなく、解決が難しいコンパイルの問題や予期しない実行時の結果が発生する可能性があります。これを軽減するには、次のものが必要です。
      • 長く、あいまいな、および/または中央で調整された識別子、およびそれらへのアクセスは、使用済み/現在/ケーニッヒ検索された名前空間、名前空間エイリアスなどを暗黙的に一致させることから利益を得ることができません。
      • すべての大文字の使用は一般に必要であり、プリプロセッサ定義用に予約されています(エンタープライズ規模のプリプロセッサの使用を管理しやすくするための重要なガイドラインであり、サードパーティのライブラリが従うことが期待できます)。これを観察すると、既存の定数または列挙型を定義に移行する必要があります。大文字の変更を伴います(したがって、クライアントコードに影響します)。 (個人的には、列挙型の最初の文字を大文字にしますが、constsは大文字にしないので、とにかくここでヒットします-おそらくそれを再考する時間です。)
    • より多くのコンパイル時操作が可能:文字列リテラルの連結、文字列化(そのサイズを取る)
      • 欠点は、_#define X "x"_とクライアントの使用法が_"pre" X "post"_の場合、Xを定数ではなく実行時に変更可能な変数にする必要がある場合に問題が発生することですが、その移行は_const char*_または_const std::string_は、既にユーザーに連結操作を組み込むように強制している場合。
    • 定義された数値定数でsizeofを直接使用することはできません
    • untyped(GCCはunsignedと比較しても警告しません)
    • 一部のコンパイラ/リンカー/デバッガチェーンは識別子を提示しない可能性があるため、「マジックナンバー」(文字列など)を確認することになります。
    • アドレスを取得できません
    • 置換された値は、使用の各ポイントで評価されるため、#defineが作成されるコンテキストで正当(または離散)である必要はありません。したがって、まだ宣言されていないオブジェクトを参照でき、必要のない「実装」に依存します。事前に含めて、配列の初期化に使用できる_{ 1, 2 }_などの「定数」または_#define MICROSECONDS *1E-6_などを作成します。(間違いなくこれはお勧めしません!)
    • ___FILE___や___LINE___のようないくつかの特別なものをマクロ置換に組み込むことができます
  • 列挙型
    • 整数値でのみ可能
    • 適切にスコープされた/識別子の衝突の問題はうまく処理されました
    • 強く型付けされていますが、制御できない十分な大きさのsignedまたはunsigned intサイズ(C++ 03)
    • アドレスを取得できません
    • より強力な使用制限(例:インクリメント-template <typename T> void f(T t) { cout << ++t; }はコンパイルされません)
    • 各定数の型は囲んでいる列挙型から取得されるため、template <typename T> void f(T)は、異なる列挙型から同じ数値が渡されると、異なるインスタンス化を取得します。これらはすべて、実際のf(int)インスタンス化とは異なります。 。
    • typeofを使用しても、numeric_limitsが有用な洞察を提供することを期待することはできません
    • 列挙型のタイプ名は、RTTI、コンパイラメッセージなどのさまざまな場所に表示される可能性があります-おそらく有用であり、おそらく難読化されています

原則として、私はconstsを使用し、それらを一般的な使用法の最も専門的なオプションと見なします(ただし、他のconstsは、この古い怠惰なプログラマーにとって魅力的な単純さを持っています)。

2
Tony Delroy

マクロはスコープを尊重せず、マクロの名前はシンボリックデバッガーで使用できない場合があります。 Dan Saks には、マクロ(なし)、定数オブジェクト、および列挙定数の相対的なメリットに関するかなり完全な記事があります。 Stephen Dewhurst のように、Saksは、ストレージを使用しないため、整数値の列挙定数を優先します(より正確には、列挙定数にはストレージ期間もリンケージもありません)。

0
Gnawme

defineは再定義できますが、constはコンパイラエラーの原因になります。

サンプル:ソース:main.cpp

#define int_constance 4
#define int_constance 8 // ok, compiler will warning ( redefine macro)

const int a = 2;
const int a = 4; // redefine -> error

int main(int argc, char** argv)
{
   std::cout << int_constance ; // if remove second #define line, output will be 8

   return 0;
}
0
Phạm Mạnh