C/C++前処理とマクロの高度な使い方

3257 ワード

プリプロセッシング(preprocessing)は主にテキスト置換のプロセスである.
  • すべての#defineを削除し、すべてのマクロ定義を展開します.はっきり言って、文字置換
  • です.
  • はすべての条件コンパイル命令を処理し、#ifdef#ifndef#endifなどは、#付きの
  • である.
  • は、includeを処理する、行
  • にincludeが指すファイルを挿入する.
  • すべてのコメントを削除
  • 行番号とファイルの表示を追加し、デバッグとコンパイルエラーが発生したときに、どのファイルのどの行であるかを知ることができます.
  • は、コンパイラが
  • を使用する必要があるため、pragmaコンパイラ命令を保持する.
    1.defineの基本的な使い方
    #define PI 3.1415
    

    前処理時に発生したすべてのPIが3.1415に置き換えられます.
    #define A 32+6
    int i = 6*A;
    

    前処理時には6*38ではなくint i = 6*32 + 6;になります.
    だから純粋なテキストの置換で、簡単な計算の結果を計算することはできません.
    2.繰り返し定義
    もし私たちがvehicleを定義するならh、vehicle類が入っています.carでhで定義carクラスはvehicleから継承され、#include "vehicle.h"はcar.cppで両者を引用すると
    #include "vehicle.h"
    #include "car.h"
    

    定義を繰り返すエラーが発生します.vehicle.hはcpp上に埋め込まれると2回埋め込まれる.解決方法は二つある:(1).使用#ifdef#ifndef#endif
    #ifndef VEHICLE_H
    #define VEHICLE_H
    class vehicle{...}
    #endif
    

    このように2回埋め込むと、2回目は#ifndef VEHICLE_Hに入り、定義されていることに気づき、#endifの後ろに直接ジャンプします.(2). pragma onceを使用してincludeの後ろにある行#pragma onceのコンパイラbuildに続くと、重複したヘッダファイルを直接削除するので手間が省けますが、debugのときにコンパイラがヘッダファイルの埋め込みを隠しているため、エラーをチェックするときに不便です.
    3.define単純関数
    #define MAX(a,b) a>b ? a : b
    

    これにより、MAX関数を定義し、2つのパラメータのうち大きいものを返すことができます.長所はタイプを無視して、何でも伝わる、templateに似ています.(この点も利点ではなくデメリットである可能性があります)欠点は、次のコードが表示されます.
    int i = MAX(7*6, 2);
    

    前処理されると
    int i = 7*6 > 2 ? 7*6 : 2;
    

    これにより7*6は2回計算され,1回以上計算が繰り返され,効率が低下する.
    3.#defineの高度な使い方
    defineマルチラインコードセグメントを考えたらどうしますか?「」を使用して、「」を行末に添付すると、前処理時に複数の行を1行にマージでき、可読性を維持し、defineに成功します.
    #define CLASS_DECLARE(c)\
    const char* GetName(){\
        return #c;}\ 
    static int s_num;\
    int attribute1;\
    int attribute2
    

    注意:#番号は、送信されたパラメータcのタイプをstringに変えます.すなわち、送信されたcのタイプのstringを返します.たとえば、CLASS_DECLARE(new Car())を呼び出すと、「Car」というstringを返し、タイプ名が得られます.
    CLASS_は複数のクラスで繰り返し使用できます.DECLARE()は、クラスごとにGetName()という関数とs_があります.num,attribute 1,attribute 2の3つのメンバー変数.
    しかし、複数のクラスでCLASS_を使用するとDECLARE()の場合、コンパイラはs_を知らないためlink errorが表示されます.num,attribute 1,attribute 2がどのクラスに属するかをマクロで定義する場合
    #define CLASS_DEFINE(c)\
    int c::s_num = 0;\
    int c::attribute1 = 0;\
    int c::attribute2 = 0
    

    次にCarクラスを定義します
    Car.h
    ============================
    class Car
    {
      CLASS_DECLARE(Car);
    }
    
    Car.cpp
    ============================
    CLASS_DEFINE(Car);
    

    これでlink errorは現れず、CarにはGetName()という関数とs_が入っています.num,attribute 1,attribute 2の3つのメンバー変数.
    個人的には、この使い方はコードセグメントを多重化して複数のクラスを初期化することができ、C++の親の継承に似ているような気がします.しかし、親継承は理解しやすく、書きやすく、読みやすく、defineの読み取りが悪く、debugの場合、前処理テキストが置換されているため、defineの行コードにナビゲートできず、デバッグしにくい.唯一の利点は、親よりも実行効率が速く、ゲームエンジンのような比較的効率的なコードで使用される可能性があります.
    PS:マクロ定義における#番の使い方
  • if elseコードブロック
  • #define VAR 1
    #if VAR == 1
    //do something
    #elif VAR == 2
    //do something
    #else 
    //do something
    #endif
    
  • "###"接続A##BはABという文字列を返し、###と前後のスペースを削除します.
  • #define HANDLER_WRAPPER(c) \
        static void c ## Wrapper(a) {return a;}
    
    Class Car {
      HANDLER_WRAPPER(Car);//Car    CarWrapper()  
    }