7

私は適切なエラー処理が必要な C プログラムを書いています。コードは次のようになります。

If(doWork("A")<0){
    return -1;   
}
If(doWork("B")<0){
    undoWork("A");
    return -1;
}
If(doWork("C")<0){
    undoWork("A");
    undoWork("B");
    return -1;
}
return 0;

このコードは機能しますが、非常に乱雑に見えます。特に、呼び出すべき長いリストがありdoWork(X)ます。この場合、エラーを処理するためのより適切でクリーンなアプローチはありますか?

4

6 に答える 6

7

一部の人々、特に初級者から中級者のプログラマーは、goto実稼働コードを見ると非常に特異な反応を示しますが、リソースを順次取得し、エラー時にインテリジェントに解放するための通常のイディオムは次のとおりです。

if(doWork("A") < 0)
  goto errA;

if(doWork("B") < 0)
  goto errB;

if(doWork("C") < 0)
  goto errC;

/* success! */
return 0;

/* Error handling / releasing resources section */
errC:
  undoWork("B");
errB:
  undoWork("A");
errA:

return -1;

Linux カーネルなど、システム コードで多くの例を見ることができます。

于 2012-04-07T02:54:22.463 に答える
2

同じタスクdoWorkであるため、リンクされたリストまたはベクトルを定義し、jobsそれをパラメータとして に渡しdoWork、対応する情報を関数内のこのリストに追加し、undoWork1 回だけ呼び出すことができます。

If(doWork("A", &jobs)<0){
    return -1;   
}
If(doWork("B", &jobs)<0){
    undoWork(jobs);
    return -1;
}
If(doWork("C", &jobs)<0){
    undoWork(jobs);
    return -1;
}
return 0;

このように、元に戻すジョブの組み合わせに関係なく、ロジックが過度に複雑になることはありません。

@ twain249のソリューションと比較した利点は、ジョブがリストに追加されるかどうかを関数が決定するため、優れた分離、モジュール性が得られることです。

もちろん、何らかの形式の相互利用可能なデータ構造をこれと組み合わせて、繰り返しコードの量をさらに減らすことができます

for(i=0; i < jobdata.size; i++) {
    If(doWork(jobdata[i], &jobs)<0){
        undowork(jobs);
        return -1;   
    }
}

お気づきのように、データ構造の設計は、アルゴリズムの設計において重要な役割を果たします。通常、その役割は、一般に考えられているよりもはるかに重要です。

何千ものジョブが存在する可能性がありますが、コードは 4 行のままです。

于 2012-04-07T02:41:58.247 に答える
0

呼び出す必要があるすべてのものをdoWork配列に格納できる場合は、次のようにコードを大幅に短縮できます。

int i = 0;
int len = MAX_NUM; //set to the value of calls
int error = 0;

for(i = 0; i < len; i++) {
    if(doWork(a[i]) < 0) {
        error = 1;
        break;
    }
}

if(error) {
    for(int j = 0; j < i; i++) {
        undoWork(a[j]);
    }
    return -1;
}
于 2012-04-07T02:40:19.577 に答える
0

明確で goto を必要としない単一のパス ループに基づく、広く使用されている別のアプローチもあります。ただし、元に戻す機能は、実行された作業と実行されていない作業の両方を正しく処理することを意味します。

do
{
  if(doWork("A")<0)
    break;   

  if(doWork("B")<0)
    break;

  if(doWork("C")<0)
    break;

  return 0;
}
while(0);

undoWork("A");
undoWork("B");
undoWork("C");
return -1;
于 2012-04-07T03:51:40.347 に答える
0

非常に長いリストがない場合は、この方法でアプローチできます。

if (dowork("A") >=0) {
if (dowork("B") >=0) {
if (dowork("C") >=0) {
if (dowork("D") >=0) return 0;
undowork("C"); }
undowork("B"); }
undowork("A"); }
return -1;
于 2012-04-07T03:26:06.637 に答える
0

おそらくそうではありません。C++ や C# などの新しい言語では、このような状況を改善するために例外を優先します。

おそらく、実行したタスクを何らかの方法で示し、それらを元に戻すテーブルを作成できます。しかし、それはあなたのコードをより複雑にするだけでなく、より複雑にするだろうと本当に思います。

また、 を使用することにはかなり強い思い入れgotoがありますが、実際にはこのような構造を単純化できる場合もあります。

于 2012-04-07T02:31:29.073 に答える