do while(0)文の妙用

3312 ワード

C++には、for、while、doの3種類のループ文があります.しかし、一般的なアプリケーションでループを行う場合は、forとwhileを使うことができます.do...whileは相対的に重視されていない.しかし、最近私たちのプロジェクトのコードを読んだとき、doを見つけました.whileのいくつかの非常に賢い使い方は、ループではなく、コードの堅牢性を高めるために他のものとして使われています.
do{...}を使うwhile(0)構築後のマクロ定義は、カッコやセミコロンなどの影響を受けず、常に所望の方法で実行を呼び出します.doは、括弧内の論理が実行されることを保証し、while(0)は、ループがない場合と同様に、論理が一度だけ実行されることを保証することができる.Linuxと他のコード・ライブラリのマクロは、呼び出しコードでどれだけのセミコロンと括弧が使用されているかにかかわらず、実行ロジックをdo/while(0)で囲みます.
1. do...while(0)goto文を消去します.通常、1つの関数でリソースの割り当てを開始し、途中で実行中にエラーが発生した場合に関数を終了します.もちろん、終了する前にリソースを解放するコードは次のようになります.
bool Execute()
{
   //     
   int *p = new int;
   bool bOk(true);

   //          
   bOk = func1();
   if(!bOk) 
   {
      delete p;   
      p = NULL;
      return false;
   }

   bOk = func2();
   if(!bOk) 
   {
      delete p;   
      p = NULL;
      return false;
   }

   bOk = func3();
   if(!bOk) 
   {
      delete p;   
      p = NULL;
      return false;
   }

   // ..........

   //     ,       
    delete p;   
    p = NULL;
    return true;
   
}

ここで最大の問題はコードの冗長性であり、操作を増やすたびに、対応するエラー処理が必要で、非常に柔軟ではありません.そこで私たちはgotoを思い浮かべました
bool Execute()
{
   //     
   int *p = new int;
   bool bOk(true);

   //          
   bOk = func1();
   if(!bOk) goto errorhandle;

   bOk = func2();
   if(!bOk) goto errorhandle;

   bOk = func3();
   if(!bOk) goto errorhandle;

   // ..........

   //     ,       
    delete p;   
    p = NULL;
    return true;

errorhandle:
    delete p;   
    p = NULL;
    return false;
   
}

コード冗長性は解消されましたが、C++の中で身分の微妙なgoto文を導入しました.gotoを正しく使うことでプログラムの柔軟性と簡潔性を大幅に向上させることができますが、あまり柔軟なものは危険で、私たちのプログラムを混乱させます.では、どのようにgoto文を避け、コード冗長性を解消することができますか.doを見てください.while(0)サイクル:
bool Execute()
{
   //     
   int *p = new int;

   bool bOk(true);
   do
   {
      //          
      bOk = func1();
      if(!bOk) break;

      bOk = func2();
      if(!bOk) break;

      bOk = func3();
      if(!bOk) break;

      // ..........

   }while(0);

    //     
    delete p;   
    p = NULL;
    return bOk;
   
}

「きれい!」と、コードを見ればいい、何も言わないで...
2マクロ定義のdo...もしあなたがC++プログラマーなら、私はあなたが使ったことがあると信じる理由があります.あるいは接触したことがあります.少なくともMFCを聞いたことがあります.MFCのafxです.hファイルでは、多くのマクロ定義がdoを使用していることがわかります.while(0)またはdo...例えば、#define AFXASSUME(cond)do{bool_afx_condVal=!(cond);ASSERT(_afx_condVal);_analysis_assume(_afx_condVal);}while(0)ざっと見るとおかしいと思います.ループの中で一度しか実行していない以上、余計に見えるdoをください.while(0)は何の意味がありますか?もちろんあります!より明確に見えるように、ここでは簡単なマクロで説明します:#define SAFE_DELETE(p)do{delete p;p=NULL}while(0)ここでdoを外すと仮定すると...while(0), #define SAFE_DELETE(p) delete p; p = NULL; では、if(NULL!=p)SAFE_DELETE(p) else   ...do sth... 2つの問題があります.1)ifブランチの後に2つの文があるため、elseブランチに対応するifがなく、コンパイルに失敗しました.2)elseがないと仮定し、SAFE_DELETEの2番目の文はifテストに合格するかどうかにかかわらず、永遠に実行されます.この2つの問題を避けるために、私はこの難解なdoを使う必要はありません.while、私は直接{}で囲むだけでいい#define SAFE_DELETE(p) { delete p; p = NULL;} 確かに、これでは上記の問題は存在しないと思いますが、C++プログラマーにとって、各文の後ろにセミコロンを付けるのは約束の習慣だと思います.そうすると、if(NULL!=p)SAFE_DELETE(p); else   ...do sth... そのelseブランチはコンパイルできないので(原因は同じ)、do...while(0)を採用するのが良い選択です.
私たちのコードの習慣は、判断ごとに{}を加えることで、このような問題はありません.doも必要ありません.while了,如:if(...)  { } else { }
確かに、これは良い、提唱すべきプログラミング習慣ですが、一般的にこのようなマクロはlibraryの一部として現れていますが、libraryの著者にとって、彼がしなければならないのはライブラリに汎用性、強壮性を持たせることです.そのため、彼はライブラリの使用者に対する仮説、例えば符号化規範、技術レベルなどを持ってはいけません.