単一行のifまたはループに中括弧(つまり{})を使用する目的は何ですか?


321

私は私のC ++講師の講義ノートをいくつか読んでいます、そして彼は以下を書きました:

  1. インデントを使用// OK
  2. 演算子の優先順位に依存しない-常に括弧を使用する// OK
  3. 常に{}ブロックを使用します-1 行の場合でも// OKはありません。なぜですか???
  4. 比較の左側にあるConstオブジェクト// OK
  5. > = 0の変数には符号なしを使用します//素敵なトリック
  6. 削除後にポインターをNULLに設定-二重削除保護//悪くない

3番目の手法は私にははっきりしていません。1つの行を{ ... }

たとえば、次の奇妙なコードを見てください。

int j = 0;
for (int i = 0 ; i < 100 ; ++i)
{
    if (i % 2 == 0)
    {
        j++;
    }
}

そしてそれを次のものに置き換えます:

int j = 0;
for (int i = 0 ; i < 100 ; ++i)
    if (i % 2 == 0)
        j++;

最初のバージョンを使用する利点は何ですか?


256
可読性と保守性。どのステートメントブロック 'j ++'が属しているかはすぐにはわかりません。また、コードを追加した後にifステートメントと関連付けられないこともあります。
森と木

27
いくつかの理由により、これらの行に中かっこ{}を使用するよう常に指示されました。コードが読みやすくなります。また、6か月後に他の誰かがコードを編集する必要がある可能性があるため、明確にすることが重要であり、中かっこを使用するとエラーが発生する可能性が低くなります。それについて技術的にもっと正確なものは何もありません。それはより良い実践の問題です。プロジェクトには、何千人もの新しいコードが何千行にもわたる可能性があることに注意してください。
RossC 2012

30
6には同意しません。二重削除を隠し、論理エラーを隠す可能性があるからです。
Luchian Grigore 2012

28
#5はトリッキーかもしれません-このループを考えてください:for (unsigned i = 100; i >= 0; --i)
Archie

36
ところで、(i % 2 == 0)矛盾します(2)。あなたは演算子の優先順位に依存しており、その意味はもちろん((i % 2) == 0)ではありません(i % (2 == 0))。私はルール2を「有効な感情ですが、「常に」間違っている」と分類します。
スティーブジェソップ2012

回答:


509

iインクリメントするときにも変更してみましょうj

int j = 0;
for (int i = 0 ; i < 100 ; ++i)
    if (i % 2 == 0)
        j++;
        i++;

大野!Pythonから来た場合、これは問題ないように見えますが、実際にはそうではありません。

int j = 0;
for (int i = 0 ; i < 100 ; ++i)
    if (i % 2 == 0)
        j++;
i++;

もちろん、これはばかげた間違いですが、経験豊富なプログラマでさえも起こり得る間違いです。

別の非常に良い理由は、ta.speot.isの回答で指摘されています

私が考えることができる3番目のものは入れ子ifのものです:

if (cond1)
   if (cond2) 
      doSomething();

さて、あなたが今会いたくないdoSomethingElse()ときにしたいと思いますcond1(新機能)。そう:

if (cond1)
   if (cond2) 
      doSomething();
else
   doSomethingElse();

これ以降、明らかに間違っているelse、内側に関連付けますif


編集:これは注目を集めているので、私の見方を明確にします。私が答えていた質問は:

最初のバージョンを使用する利点は何ですか?

私が説明した。いくつかの利点があります。ただし、IMOの「常に」のルールは常に適用されるわけではありません。だから私は完全にサポートしていません

常に{}ブロックを使用します-1行でも// OKではありません。なぜですか???

いつも{}ブロックを使うと言っいるのではありません。それが十分に単純な条件と振る舞いであるなら、そうしないでください。誰かが後で来るかもしれないと疑い、機能を追加するようにコードを変更する場合は、実行してください。


5
@Science_Fiction:True、ただしi++ before を追加した場合j++、両方の変数が使用されたときもスコープ内にあります。
マイクシーモア

26
これは非常に合理的に聞こえますが、編集者があなたではなくインデントを行うという事実を無視しi++;、ループの一部ではないことをすぐに示す方法でインデントします。(以前は、これは合理的な議論であった可能性があり、私はそのような問題を見てきました。約20年前。それ以降ではありません。)
James Kanze

43
@ジェームズ:それは「事実」ではありませんが、それはあなたのワークフローです。そして、多くの人のワークフローですが、全員のワークフローではありません。フォーマットルールを適用するWYSIWYGエディター(vi / emacs / Visual Studio)の出力ではなく、C ++ソースをプレーンテキストファイルとして扱うのは必ずしもエラーではないと思います。したがって、このルールは、必要なものを超えてエディターに依存しませんが、人々が実際にC ++を編集するために使用するものを超えません。したがって、「防御的」です。
スティーブジェソップ2012

31
@JamesKanze誰もが常に強力なIDEで動作するという仮定に本当に依存していますか?最後に書いたCIはNanoでした。それでも、IDEで最初にオフにしがちなことの1つは自動インデントです-IDEは非線形ワークフローの邪魔をする傾向があり、不完全な情報に基づいて「ミス」を修正しようとするためです。IDEは、すべてのプログラマーの自然なフローの自動インデントにあまり長けていません。このような機能を使用するプログラマーは、スタイルをIDEにマージする傾向があります。これは、1つのIDEのみを使用している場合は問題ありませんが、多数のIDEを使用している場合は問題ありません。
Rushyo 2012

10
「これはばかげた間違いですが、経験豊富なプログラマでさえも起こり得る間違いです。」–私が答えで言ったように、私はそれを信じていません。それは、実際には問題を引き起こさない、完全に人為的なケースだと思います。
コンラートルドルフ

324

{andを使用しない場合、コメントを使用して制御フローを誤って変更することは非常に簡単です}。例えば:

if (condition)
  do_something();
else
  do_something_else();

must_always_do_this();

あなたがコメントアウト場合はdo_something_else()1行コメントで、あなたはこれで終わるだろう:

if (condition)
  do_something();
else
  //do_something_else();

must_always_do_this();

コンパイルされますが、must_always_do_this()常に呼び出されるわけではありません。

コードベースにこの問題があり、リリース前に誰かが一部の機能を非常に早く無効にしていた。幸い、私たちはコードレビューでそれを見つけました。


3
おお!must_always_do_this();// do_something_else();をコメントした場合に実行される定義済みの動作です。
Mr.Anubis 2012

1
@Supr、それが最初に書かれたように、中括弧を使用する場合、正しいフローを壊すのは難しいと彼は言っており、次にコードを適切に括弧に入れずに壊すのがいかに簡単であるかの例を示します
SeanC

20
先日これに遭遇しました。if(debug) \n //print(info);。基本的にライブラリ全体を取り出しました。
ケビン

4
Fortunately we caught it in code review.痛い!それはとても間違っているようです。Fortunately we caught it in unit tests.はるかに良いでしょう!
BЈовић

4
@BЈовићしかし、コードが単体テストに含まれていた場合はどうなりますか?心が揺れる。(冗談ですが、これはレガシーアプリです。単体テストはありません。)
ta.speot.is

59

講師の能力に疑問があります。彼のポイントを考慮して:

  1. OK
  2. 誰かが本当に書いたり(または読みたい)(b*b) - ((4*a)*c)でしょうか?いくつかの優先順位は明らかであり(またはそうである必要があります)、余分な括弧は混乱を増すだけです。(一方、括弧が不要であることを知っていても、あまり明確でない場合は括弧を使用する必要があります)。
  3. ちょっと。条件文とループの書式設定には、2つの広義の規則があります。
    if(cond){
        コード;
    }
    
    そして:
    if(cond)
    {
        コード;
    }
    
    最初に、私は彼に同意します。開口部{はそれほど見えないので、常にそこにあると想定するのが最善です。ただし、2番目の方法では、私(およびこれまでに作業したほとんどの人々)は、単一のステートメントで中括弧を省略しても問題はありません。(もちろん、インデントが体系的であり、このスタイルを一貫して使用していることを前提としています(そして、非常に読みやすいコードを記述している多くの非常に優れたプログラマーは、最初の方法でフォーマットする場合でも中括弧を省略します)。
  4. いいえ。のようなものif ( NULL == ptr )は読みにくくなるほど醜いです。比較を直感的に記述します。(多くの場合、結果は右側の定数になります。)彼の4は悪いアドバイスです。コードが不自然になると、コードが読みにくくなります。
  5. いいえ。何もなく、はint、特別な場合のために予約されています。経験豊富なCおよびC ++プログラマにとって、unsignedシグナルビット演算子の使用。C ++には実際の基本型(または他の有効な部分範囲型)はありません。unsignedプロモーションルールのため、数値では機能しません。シリアル番号のように、算術演算が意味をなさない数値は、おそらくですunsigned。ただし、これは間違ったメッセージを送信するため、これには反対です。ビット単位の演算も意味がありません。基本的な規則は、整数型はintであるということです。ただし、別の型を使用する重要な理由はありません。
  6. いいえ。これを体系的に行うことは誤解を招きやすく、実際には何も保護しません。厳密なOOコードでdelete this;は、これが最も頻繁に発生します(そしてに設定thisすることはできませんNULL)。それ以外の場合、ほとんどdeleteがデストラクターにあるため、後でポインターにアクセスできません。そして、それを設定することNULLは、周りに浮かんでいる他のいかなるポインタについても何もしません。体系的にポインターを設定するとNULL、誤った安心感が与えられ、実際には何も購入されません。

典型的な参考文献のコードを見てください。たとえば、Stroustrupは最初のルールを除いて、指定したすべてのルールに違反します。

別の講師を見つけることをお勧めします。彼が話していることを実際に知っている人。


13
番号4は見苦しいかもしれませんが、目的はあります。if(ptr = NULL)を防止しようとしています。今まで使用したことがないと思いますdelete thisが、見たよりも一般的ですか?使用後にポインタをNULLに設定することは、YMMV以外に悪いことだとは思わない傾向があります。多分それは私だけですが、彼のガイドラインのほとんどはそれほど悪くないと思われます。
Firedragon 2012

16
@Firedragon:ほとんどのコンパイラは、if (ptr = NULL)次のように記述しない限り警告しますif ((ptr = NULL))。James KanzeがNULL最初に持っていることの醜さはそれを私にとって明確なNOにすることに同意する必要があります。
レオ

34
@JamesKanze:私がここで述べたことのほとんどに同意しないと言わざるを得ません-私はそれらに到達したことに対するあなたの主張を高く評価し、尊重します。経験豊富なCおよびC ++プログラマにとって、符号なしシグナルビット演算子の使用。-私は全く同意しない:使用ビット演算子はビット演算子の使用を通知します。私にとって、の使用は、変数が正の数のみを表すべきであるというプログラマー側の願望unsigned示しています。符号付きの数値と混合すると、通常、講師が意図していたコンパイラ警告が表示されます。
コンポーネント10

18
経験豊富なCおよびC ++プログラマーにとって、符号なしシグナルビット演算子の使用の有無についてsize_t、 誰でも?
ta.speot.is 2012

16
@James Kanze、目的を検討してください。あなたは経験豊富なプログラマーが作成したコードを教育的な例と比較しています。これらの規則は、生徒が犯すと思われる種類の間違いであるため、講師によって提供されます。経験があれば、生徒はこれらの絶対的な要素をリラックスしたり無視したりできます。
Joshua Shane Liberman

46

他のすべての回答は、講師のルールを守ります3。

私はあなたに同意すると言いましょう:ルールは冗長であり、私はそれを助言しません。それは、それが本当だ理論的に常に中かっこを追加するはエラーを防ぐことができます。一方、私は実際にこの問題に遭遇したことはありません。他の答えが意味するところとは逆に、中括弧が必要になったときに、中括弧を追加するのを忘れたことはありません。適切なインデントを使用すると、複数のステートメントがインデントされた後に中括弧を追加する必要があることがすぐに明らかになります。

「コンポーネント10」による回答は、実際にこれが本当にエラーにつながる可能性のある唯一の考えられるケースを強調しています。しかしその一方で、正規表現を使用してコードを置き換えると、とにかく常に細心の注意が必要になります。

メダルの反対側を見てみましょう:中括弧を常に使用することには不利な点がありますか?他の答えは単にこの点を無視します。しかし、そこにあります短所:それは垂直方向の画面スペースの多くを占めており、それはあなたが必要以上にスクロールしなければならないことを意味するので、これは順番に、あなたのコードが読めなくすることができます。

最初に多くのガード句がある関数を考えてください(そうです、以下は悪いC ++コードですが、他の言語ではこれはかなり一般的な状況です):

void some_method(obj* a, obj* b)
{
    if (a == nullptr)
    {
        throw null_ptr_error("a");
    }
    if (b == nullptr)
    {
        throw null_ptr_error("b");
    }
    if (a == b)
    {
        throw logic_error("Cannot do method on identical objects");
    }
    if (not a->precondition_met())
    {
        throw logic_error("Precondition for a not met");
    }

    a->do_something_with(b);
}

これは恐ろしいコードであり、以下の方がはるかに読みやすいと強く主張します。

void some_method(obj* a, obj* b)
{
    if (a == nullptr)
        throw null_ptr_error("a");
    if (b == nullptr)
        throw null_ptr_error("b");
    if (a == b)
        throw logic_error("Cannot do method on identical objects");
    if (not a->precondition_met())
        throw logic_error("Precondition for a not met");

    a->do_something_with(b);
}

同様に、入れ子にされた短いループは、中括弧を省略することでメリットを得られます。

matrix operator +(matrix const& a, matrix const& b) {
    matrix c(a.w(), a.h());

    for (auto i = 0; i < a.w(); ++i)
        for (auto j = 0; j < a.h(); ++j)
            c(i, j) = a(i, j) + b(i, j);

    return c;
}

と比べて:

matrix operator +(matrix const& a, matrix const& b) {
    matrix c(a.w(), a.h());

    for (auto i = 0; i < a.w(); ++i)
    {
        for (auto j = 0; j < a.h(); ++j)
        {
            c(i, j) = a(i, j) + b(i, j);
        }
    }

    return c;
}

最初のコードは簡潔です。2番目のコードは肥大化しています。

そして、はい、これは前の行に開始中かっこを置くことである程度緩和できます。しかし、それでも、中括弧がないコードよりも読みにくくなります。

つまり、画面スペースを占有する不要なコードを記述しないでください。


26
画面スペースを不必要に占有するコードを書くことを信じなければ、開始ブレースを独自の行に置くビジネスはありません。私はおそらく今、GNUの神聖な復讐から立ち直らなければならないでしょうが、真剣に-コードを垂直方向にコンパクトにしたいか、そうしないかです。また、コードを縦方向にコンパクトにすることのみを目的として設計されたものを実行しないでください。しかし、あなたが言うように、それを修正した後、余分な中括弧を削除したいと思うでしょう。または、たぶんif (a == nullptr) { throw null_ptr_error("a"); }1行で記述します。
スティーブジェソップ2012

6
@Steve実際のところ、あなたが述べたまさにその理由のために、私前の行にオープニングブレースを置きます。ここでは他のスタイルを使用して、違いがどれほど極端かを明確にしました。
Konrad Rudolph

9
+1私は完全にあなたの最初の例が中括弧なしで読む方がはるかに簡単であることに同意します。2番目の例では、私の個人的なコーディングスタイルは、内側のforループではなく、外側のforループで中かっこを使用することです。@SteveJessopは、垂直方向にコンパクトなコードを極端にする必要があるかどうかについて意見を異にします。縦のスペースを減らすために、ワンライナーで余分なブレースを省略していますが、ブレースを並べるとスコープが見やすくなるので、新しいブレースを新しい行に配置します。目標は読みやすさであり、垂直方向のスペースを多く使用することもあれば、少ないスペースを使用することもあります。
Travesty3 2012

22
「私は実際にこの問題に遭遇したことがありません」:ラッキー。このようなことはあなたを焼くだけでなく、90%の3度の火傷を与えます(これは、夜遅くの修正を要求する管理のいくつかの層にすぎません)。
Richard

9
@リチャード私は単にそれを買わない。チャットで説明したように、このエラーが発生する必要がある場合でも(私がそうする可能性は低いと思います)、スタックトレースを見れば修正は簡単です。なぜなら、コードを見ただけでエラーがどこにあるかは明らかだからです。あなたの誇張された主張は完全に根拠がないです。
Konrad Rudolph

40

私が取り組んでいるコードベースは、中かっこに病的な嫌悪感を抱く人々によってコードが散らばっています。後に来る人々にとって、それは本当に保守性に違いをもたらすことができます。

私が遭遇した最も頻繁な問題のある例はこれです:

if ( really incredibly stupidly massively long statement that exceeds the width of the editor) do_foo;
    this_looks_like_a_then-statement_but_isn't;

したがって、私が来て、thenステートメントを追加したい場合、注意しなければ、簡単にこれで終了してしまいます。

if ( really incredibly stupidly massively long statement that exceeds the width of the editor) do_foo;
{
    this_looks_like_a_then-statement_but_isn't;
    i_want_this_to_be_a_then-statement_but_it's_not;
}

中かっこを追加するのに最大1秒かかり、少なくとも数分の混乱したデバッグを節約できるとすれば、あいまいさを減らしたオプションを使用しないのはなぜですか?私には偽の経済のようです。


16
この例の問題は、不適切なインデントと中括弧ではなく長すぎる行にありませんか?
Honza Brabec 2012

7
はい。しかし、人々が他のガイドライン(長すぎるラインを持たないなど)も遵守していると想定した場合にのみ「安全」である設計/コーディングのガイドラインに従うと、問題が発生するようです。中かっこが最初から挿入されていた場合、この状況でifブロックが正しくなくなることはありません。
ジャム

16
中括弧を追加するにはどうすればよいでしょうか(if(really long...editor){ do_foo;}この場合は回避できるでしょうか?問題は今でも同じようです)個人的には不要な場合は中括弧を避けた方がいいですが、書くのに必要な時間とは関係ありませんが、読みやすさが低下しますコードに2行追加されているため
Grizzly 2012

1
良い点-中かっこの使用を強制すると、それらが賢明な場所に配置されることになると想定していましたが、当然ながら、例を難しくすることを決心した人がそれらをインラインに配置することもできます。しかし、ほとんどの人はそうはしないと思います。
ジャム

2
ファイルに触れるときに最初と最後に行うことは、自動フォーマットボタンを押すことです。これらの問題のほとんどが解消されます。
ジョナサンアレン

20

私の2c:

インデントを使用する

明らかに

演算子の優先順位に依存しない-常に括弧を使用する

私は「決して」と「常に」という言葉は使用しませんが、一般的にはこのルールが役立つと思います一部の言語(Lisp、Smalltalk)では、これは問題ではありません。

常に{}ブロックを使用します-1行の場合でも

私はそれをしたことはなく、単一の問題を抱えたことはありませんが、それが生徒にとってどのように役立つかを見ることができます。彼らが以前にPythonを勉強した場合。

比較の左側にあるconstオブジェクト

依田状態?いいえ、お願いします。読みやすさが損なわれます。コードをコンパイルするときは、最大警告レベルを使用してください。

0以上の変数には符号なしを使用します

OK。面白いことに、Stroustrupが反対することを聞いたことがあります。

削除後にポインターをNULLに設定-二重削除保護

悪いアドバイス!削除されたオブジェクトまたは存在しないオブジェクトを指すポインターは決してありません。


5
最後のポイントだけで+1。生のポインタは、とにかくビジネスを所有するメモリを持っていません。
Konrad Rudolph

2
unsignedの使用に関して:Stroustrupだけでなく、K&R(C)、Herb Sutter、および(私は思うに)Scott Meyers。実際、C ++のルールを本当に理解している人がunsignedを使用することについて主張するのを聞いたことはありません。
James

2
@JamesKanze実際、私がStroustrupの意見を聞いたのと同じ機会に(2008年のボストン会議)、Herb Sutterがそこにいて、その場でBjarneに反対しました。
Nemanja Trifunovic

3
unsigned壊れている」を完了するために、問題の1つは、C ++が類似のサイズの符号付き型と符号なし型を比較す​​るときに、比較を行う前に符号なし型に変換することです。その結果、値が変化します。署名済みへの変換は、必ずしもはるかに優れているとは限りません。比較は、実際には両方の値がどちらかの型のすべての値を表すことができるより大きな型に変換されたかのように行われる必要があります。
James Kanze、2012

1
@SteveJessop私はあなたが関数を返すというコンテキストでそれを取る必要があると思いますunsigned。私は彼がexp(double)値を返すことに問題がないと確信していますMAX_INT:-)。しかし、再び、本当の問題は暗黙の変換です。int i = exp( 1e6 );完全に有効なC ++です。Stroustrupは、ある時点で非可逆の暗黙の変換を廃止することを提案しましたが、委員会は興味を持っていませんでした。(興味深い質問:unsigned-> intは非可逆と見なされます。- unsigned> intint->の両方を非可逆と見なしunsignedます。これはunsignedOKにするのに長い道のりです
James Kanze、

18

より直感的で簡単に理解できます。それは意図を明確にします。

そしてそれは、新しいユーザーが知らないうちに逃す可能性がある場合、コードが破損しないことを保証し{}新しいコードのステートメントを追加しています。


Makes the intent clear+1、これがおそらく最も簡潔で正確な理由です。
Qix-モニカは

13

上記の非常に賢明な提案に追加して、これが重要になる場所のいくつかのコードをリファクタリングしているときに遭遇した1つの例は次のとおりです。最初のAPIには、会社IDを次のように設定する呼び出しがありました。

setCompIds( const std::string& compId, const std::string& compSubId );

一方、置換には2つの呼び出しが必要でした。

setCompId( const std::string& compId );
setCompSubId( const std::string& compSubId );

非常に成功した正規表現を使用してこれを変更することにしました。また、コードをastyleに渡しました。これにより、コードが非常に読みやすくなりました。次に、レビュープロセスの途中で、一部の条件付きの状況では、これを変更していることがわかりました。

if ( condition )
   setCompIds( compId, compSubId );

これに:

if ( condition )
   setCompId( compId );
setCompSubId( compSubId );

これは明らかに必要なものではありません。私は最初に戻ってこれをやり直して、置換を完全にブロック内として扱い、最終的に間抜けに見えたものを手動で変更する必要がありました(少なくともそれは正しくありません)。

astyleに--add-bracketsブラケットがない場合にブラケットを追加できるオプションがあることに気づきました。私と同じ位置にいる場合は、これを強くお勧めします。


5
私はかつて「Microsoftligent」というすばらしい造語を含んだドキュメントを見たことがあります。はい、グローバル検索と置換で重大なミスをする可能性があります。つまり、グローバルな検索と置換は、マイクロソフトの知識ではなく、インテリジェントに使用する必要があるということです。
ピートベッカー

4
これは私の死後のことではないことはわかっていますが、ソースコードでテキスト置換を行う場合は、確立されている種類のテキスト置換に使用するのと同じ規則に従って行う必要があります。言語で:マクロ。#define FOO() func1(); \ func2(); (バックスラッシュの後に改行を入れて)マクロを書くべきではありません。これは検索と置換についても同じです。とは言っても、スタイルルールとして「常に中かっこを使用する」は、すべてのマルチステートメントマクロをでラップする手間を省くため、スタイルルールとして進んだものdo .. while(0)です。しかし、私は同意しません。
スティーブジェソップ2012

2
ところで、それは日本のイタドリがよく確立されているという意味で「よく確立されています」:マクロとテキスト置換を使用するために私たちの方法から出るべきだと言っているわけではありませんが、そのようなつまり、特定のスタイルルールがコードベース全体に正常に適用された場合にのみ機能するものを実行するのではなく、機能する方法で実行する必要があります:-)
Steve Jessop

1
@SteveJessopまた、ブレースとベルトを主張することもできます。このようなマクロを使用する必要がある場合(そしてC ++との前inlineに使用しました)do { ... } while(0)、必要に応じてトリックを使用して(可能な限り多くの余分な括弧を使用して)、それらをできるだけ関数のように機能させることを目指します。家のスタイルなら、どこでも中かっこを使用するのをやめないでください(FWIW:ここで説明したすべてのスタイルをカバーする、さまざまな家のスタイルの場所で作業しました。深刻な問題であるとは決して知りませんでした。)
James Kanze

1
そして、私が考えているのは、使用するスタイルが多いほど、コードを注意深く読み、編集することです。そのため、最も読みやすいものを好みがある場合でも、他の文書を正常に読み取ることができます。私は、さまざまなコンポーネントがさまざまなチームによってさまざまな「ハウススタイル」で記述されている会社で働いていました。正しい解決策は、グローバルスタイルを作成しようとせずに、ランチルームでそれについて不満を言うことです。
スティーブジェソップ2012

8

{}明らかな場合を除いて、どこでも使用しています。単一行は次のいずれかの場合です。

if(condition) return; // OK

if(condition) // 
   return;    // and this is not a one-liner 

戻る前にメソッドを追加すると、害を及ぼす可能性があります。インデントは、条件が満たされたときにリターンが実行されていることを示しますが、常にリターンします。

ステートメントを使用したC#の他の例

using (D d = new D())  // OK
using (C c = new C(d))
{
    c.UseLimitedResource();
}

これは

using (D d = new D())
{
    using (C c = new C(d))
    {
        c.UseLimitedResource();
    }
}

1
usingステートメントでカンマを使用するだけで、必要はありません:)
Ry-

1
@minitechこれは単純にここでは機能しません。タイプが等しい場合にのみカンマを使用できます。タイプが等しくない場合は使用できません。Lukasのこれを行う方法は標準的な方法です。IDEはこれを別の方法でフォーマットします(2番目のの自動インデントがないことに注意してくださいusing)。
Konrad Rudolph

8

私が考えることができる最も適切な例:

if(someCondition)
   if(someOtherCondition)
      DoSomething();
else
   DoSomethingElse();

どちらifelseペアになりますか?インデントは、アウターifがを取得することを意味しelseますが、コンパイラーが実際にそれを認識する方法ではありません。内側の ifでしょうelse、と外ifありません。このコードがなぜあなたの期待に反しているのかを調べて理解するには、それを知っている必要があります(またはデバッグモードでそのように動作することを確認してください)。Pythonを知っていると、さらに混乱します。その場合、インデントはコードブロックを定義することがわかっているので、インデントに従って評価することが期待されます。ただし、C#は空白について空中フリップを行いません。

とはいえ、この「常に角かっこを使用する」というルールには特に同意しません。これにより、コードが垂直方向に非常にノイズが多くなり、コードをすばやく読み取ることができなくなります。ステートメントが次の場合:

if(someCondition)
   DoSomething();

...それはこのように書かれるべきです。「常に角かっこを使用する」という文は、「常に算術演算を括弧で囲む」のように聞こえます。それは非常に単純なステートメントをにa * b + c / d変え((a * b) + (c / d))、閉じかっこ(多くのコーダーの悩みの種)を見逃す可能性を導入し、何のために?操作の順序はよく知られており、強化されているため、括弧は冗長です。a * (b+c) / dたとえば、通常適用されるのとは異なる操作の順序を強制するために、括弧のみを使用します。ブロックブレースも同様です。これらを使用して、デフォルトとは異なり、「自明」ではない(主観的ですが、通常はかなり常識)場合に何をしたいかを定義します。


3
@AlexBrown ...それはまさに私のポイントでした。OPで述べられているルールは、「単一行であっても常に角かっこを使用する」というものであり、私が述べた理由でこれに同意しません。括弧、コードがインデントされた方法で動作しないため、最初のコード例に役立ちます。ブラケットを使用elseしてif、を2番目ではなく1番目とペアにする必要があります。反対票を削除してください。
KeithS 2012

5

答えを調べて、私が習慣とする種類の習慣を明確に述べていないのは、あなたのコードの物語です。

int j = 0;
for (int i = 0 ; i < 100 ; ++i)
{
    if (i % 2 == 0)
    {
        j++;
    }
}

になる:

int j = 0;
for (int i = 0 ; i < 100 ; ++i)
{
    if (i % 2 == 0) j++;
}

パッティングj++同じ行誰にも知らせる必要があるかのように、「私は今までインクリメントにこのブロックをしたいですj。もちろん、これは、行が可能な限り単純化されている場合にのみ価値があります。ペリに言及されているように、ここにブレークポイントを置くことはあまり役に立ちません。

実際、私はJavaでこの「ソート」されたコードを持つTwitter Storm APIの一部に出くわしました。このスライドショーの43ページに、実行コードからの関連するスニペットがあります

...
Integer Count = counts.get(word);
if (Count=null) count=0;
count++
...

forループブロックには2つのものが含まれているため、そのコードをインライン化しません。すなわち決して

int j = 0;
for (int i = 0 ; i < 100 ; ++i) if (i % 2 == 0) j++;

それはひどいですし、それが(意図したとおりに)機能するかどうかさえわかりません。これを行わないでください。新しい行と中括弧は、散文でコンマまたはセミコロンが行うのと同じように、関連している別々のコードを区別するのに役立ちます。上記のブロックは、いくつかの句と、別の部分を区別するために中断または一時停止しないいくつかの他のステートメントを含む非常に長い文と同じくらい悪いです。

あなたが本当に誰かに電報を送りたいなら、それは一行のみの仕事であり、三項演算子または?:フォームを使います:

for (int i = 0 ; i < 100 ; ++i) (i%2 ? 0 : >0) j++;

しかし、これはコードゴルフに近づきつつあり、私は素晴らしい習慣ではないと思います(私がj ++を片側に置くべきかどうかは私には明確ではあり:ません)。注意: C ++で三項演算子を実行したことがないので、これが機能するかどうかはわかりませんが、存在します。

要するに:

読者(つまり、コードを保守している人)があなたのストーリー(コード)をどのように解釈するか想像してみてください。それらをできるだけ明確にします。初心者のコーダー/学生がこれを維持していることを知っている場合{}は、混乱しないように、できるだけ多くを残しておいてください。


1
(1)ステートメントを同じ行に置くと、読みにくくなります。特に単純な考えは、増分のように見落とされがちです。新しい行に入れてください。(2)もちろん、forループを単一の行に置くことができますが、なぜこれがうまくいかないのですか?中括弧を省略できるのと同じ理由で機能します。C ++では、改行は重要ではありません。(3)条件演算子の例は、恐ろしいだけでなく、無効なC ++です。
Konrad Rudolph

@KonradRudolphありがとう、私はC ++で少し錆びています。(1)の方が読みやすいとは言いませんでしたが、そのコードの一部がオンラインで1行になるように意図されていたことを示しています。(2)私のコメントは、私がそれを読んでそれが機能することをまったく理解できなかったか、または意図したとおりであるかを超えていました。これは、この理由ですべきでないことの例です。(3)ありがとう、私は長い間C ++を書いていません。今すぐ修正します。
Pureferret

2
また、1行に複数の式を入れると、コードのデバッグが難しくなります。その行の2番目の式にブレークポイントをどのように配置しますか?
Piotr Perak 2012

5

せず{}に2つのステートメントがある場合、問題を見逃しがちです。コードが次のようになっているとします。

int error = 0;
enum hash_type hash = SHA256;
struct hash_value *hash_result = hash_allocate();

if ((err = prepare_hash(hash, &hash_result))) != 0)
    goto fail;
if ((err = hash_update(&hash_result, &client_random)) != 0)
    goto fail;
if ((err = hash_update(&hash_result, &server_random)) != 0)
    goto fail;
if ((err = hash_update(&hash_result, &exchange_params)) != 0)
    goto fail;
    goto fail;
if ((err = hash_finish(hash)) != 0)
    goto fail;

error = do_important_stuff_with(hash);

fail:
hash_free(hash);
return error;

元気そうです。特にコードを含む関数がはるかに大きい場合、この問題は見逃しがちです。問題は、goto fail無条件に実行されることです。これがどれほど苛立たしいかは簡単に想像できます(hash_updateすべてにおいてhash_update機能上問題がないように見えた後、lastが常に失敗する理由を尋ねます)。

しかし、それは私が{}どこにでも追加することを意味するわけではありません(私の意見では、{}どこでも見るのは面倒です)。問題が発生する可能性はありますが、私の個人のコーディングスタイルでは条件{}が同じ行にない場合を除いて禁止されているため、自分のプロジェクトでは発生しませんでした(そうです、私のコーディングスタイルは型破りであることに同意しますが、私は気に入っています。他のプロジェクトに貢献するときは、プロジェクトのコードスタイルを使用します)。これにより、次のコードが正常になります。

if (something) goto fail;

しかし、次のものではありません。

if (something)
    goto fail;

丁度。(完全に不要な)改行+インデントを入れないでください。この問題を完全に回避し、誰もがすぐに立ち上がることができるようにします。
アレクサンダー-モニカを復活させる'05年

4

ループと条件付きブロックのスコープを明確に定義することで、コードを読みやすくします。また、偶発的なミスからあなたを救います。


4

wrt 6:nullポインタの削除は何もしないので、より安全です。したがって、偶然にそのパスを2度通過しても、メモリ破損が原因で、解放されているメモリまたは別のメモリに割り当てられているメモリが解放されることはありません。

これは、存続期間が明確ではなく、破棄された後に再作成されることが知られている静的ファイルスコープオブジェクトとシングルトンの問題のほとんどです。

ほとんどの場合、auto_ptrsを使用することで、この必要性を回避できます。


6
そのパスを2度通過すると、プログラミングエラーが発生します。ポインタをnullに設定してこのエラーの害を少なくしても、根本的な問題は解決されません。
ピートベッカー

1
同意しましたが、これは以前に推奨されているのを見たことがあります。私はそれがいくつかのプロのプログラミング標準にあると信じています。投稿者の教授がそれを思いついた理由ではなく、理由についてもっとコメントしていました
Tom Tanner

2
ピートベッカーの発言に続いて:根本的な問題は解決しませんが、それを覆い隠す可能性があります。(NULL削除後にポインタを設定する場合NULLがあります。そのような状況でポインタが持つ正しい値です。たとえば、ポインタはキャッシュされた値を指しNULL、無効なキャッシュを示します。NULLデストラクタの最後の行へのポインタは、彼がC ++を知っているかどうか疑問に思います。)
James Kanze 2012

4

私はルチアンの受け入れられた答えが好きです。実際、彼が正しいことを難しい方法で学んだので、単一行のブロックであっても、常に中括弧を使用します。ただし、あなたの例のように、個人的にはフィルターを作成するときに例外を設けています。この:

int j = 0;
for (int i = 0 ; i < 100 ; ++i)
{
    if (i % 2 == 0)
    {
        j++;
    }
}

乱雑に見えます。これは、forループとifステートメントを別々のアクションに分離しますが、実際の目的は単一のアクションです。2で割り切れる整数をすべてカウントするためです。より表現力豊かな言語では、次のように記述できます。

j = [1..100].filter(_%2 == 0).Count

クロージャーがない言語では、フィルターを単一のステートメントで表現することはできませんが、forループの後にifステートメントを続ける必要があります。しかし、それはまだプログラマーの頭の中の1つのアクションであり、私はそれをコードに反映する必要があると信じています。

int j = 0;
for (int i = 0 ; i < 100 ; ++i)
  if (i % 2 == 0)
{
    j++;
}

7
for (int i = 0; i < 100; i += 2);インデントについての議論を続けるために、誰もがどうにか無視することが好きです;-) i「特定のプロパティを持つ特定の範囲のそれぞれについて」ロジックを表現するのに「最善」のロジックを表現するための「最善」ループなしのC ++、標準アルゴリズムのいくつかの悪夢の組み合わせを用いて、filter_iteratorおよび/またはcounting_iterator
スティーブジェソップ2012

1
また、それがあった場合、結果として生じる大規模な単一ステートメントをどのようにインデントするかについて意見が分かれる可能性があります。
スティーブジェソップ2012

2
@Steve、これは単なる例です。パターンの正当な使用法はたくさんあります。明らかに2で割り切れる1から100までの数を数えたいなら、あなたがしなければならないのは100/2だけです。
MikeFHay 2012

2
確かに、私が知っているのは、i「特定のプロパティを持つ特定の範囲のそれぞれについて」に抽象化した理由です。通常、SOでは、人々は実際の質問を無視して、与えられた例に対する完全に異なるアプローチを支持して非常に迅速です。しかし、インデントは重要なので、私たちはしません;-)
スティーブジェソップ

4

上で説明したエラーの防止に役立つオプションの1つは、中括弧を使用しない場合に何をしたいかをインライン化することです。コードを変更しようとすると、エラーに気付かないようにするのがはるかに難しくなります。

if (condition) doSomething();
else doSomethingElse();

if (condition) doSomething();
    doSomething2(); // Looks pretty obviously wrong
else // doSomethingElse(); also looks pretty obviously wrong

5
elseがに関連付けられていないため、2番目のオプションではコンパイルエラーが発生しますif
ルチアングリゴー

インラインでそれほど目に見えない問題の1つは、オートフォーマットユーティリティを使用すると、デフォルトのほとんどのIDEがインデントスタイルに変更することです。
Honza Brabec 2012

@Honza:それは非常に課された政治問題です。コードベースで共同作業をしている場合は、最後の細部まで同じインデントスタイルを使用するか、「理由」だけで既存のコードをオートフォーマットしないことに同意する必要があります。前者の場合、合意されたスタイルにこれを含めることができますが、それを尊重するようにIDEを構成するか、オートフォーマットを使用しない必要があります。共通のフォーマットが「私のIDEがオートフォーマットするものは何でも」であることに同意することは、私たち全員が同じIDEを永遠に使用する場合はすべて非常にうまくいき、そうでない場合はあまり良くありません。
スティーブジェソップ2012

3

あなたがコンパイラーなら、それは何の違いもありません。どちらも同じです。

しかし、プログラマーにとっては、最初のものはより明確で読みやすく、エラーが起こりにくいものです。


2
{とにかく、独自のラインで開く以外は。
ロブ

3

中括弧を追加する別の例。バグを検索してそのようなコードを見つけたら:

void SomeSimpleEventHandler()
{
    SomeStatementAtTheBeginningNumber1;
    if (conditionX) SomeRegularStatement;
    SomeStatementAtTheBeginningNumber2;
    SomeStatementAtTheBeginningNumber3;
    if (!SomeConditionIsMet()) return;
    OtherwiseSomeAdditionalStatement1;
    OtherwiseSomeAdditionalStatement2;
    OtherwiseSomeAdditionalStatement3;
}

メソッドを1行ずつ読むと、メソッドにtrueでない場合に返される条件があることがわかります。しかし、実際には、いくつかの条件に基づいていくつかの変数を設定する100の他の単純なイベントハンドラーのように見えます。そしてある日、Fast Coderが登場し、メソッドの最後に変数設定ステートメントを追加します。

{
    ...
    OtherwiseSomeAdditionalStatement3;
    SetAnotherVariableUnconditionnaly;
}

その結果、SomeAnditionIsMet()の実行時にSetAnotherVariableUnconditionnalyが実行されますが、すべての行のサイズがほぼ同じであり、戻り条件が垂直にインデントされていてもそれほど目立たないため、高速な人はそれに気付きませんでした。

条件付き戻りが次のようにフォーマットされている場合:

if (!SomeConditionIsMet())
{
    return;
}

それは非常に目立ち、Fast Coderは一目でそれを見つけます。


return関数に何かを追加する前に、関数の本体内で構文が強調表示されているステートメントを見つけるのが面倒な高速コーダーではない場合、高速コーダーをコードに近づけないでください。中かっこを含めることで、そのような人がコードをあちこち見回すのを止めることはできません。
cmaster-モニカを2014年

@cmaster彼はもう私たちと一緒に働いていません。とにかく、構文の強調表示は良いですが、はっきりと見えない人がいることを思い出してください(昨年、私は盲目のプログラマーからの投稿も見ました)。
Artemix 2014年

2

私は最初のものを明確にし、次に2番目のものを考える。これは、与えたコードが複雑になったときに少しのコードで、指示を閉じる感は結構です{...}、それであっても多くのことができますendifbegin...end

//first
int j = 0;
for (int i = 0 ; i < 100 ; ++i)
{
    if (i % 2 == 0)
    {
        j++;
    }
}


//second
int j = 0;
for (int i = 0 ; i < 100 ; ++i)
    if (i % 2 == 0)
        j++;
i++;

1

使い終わったら、ポインタをNULLに設定することをお勧めします。

理由は次のとおりです。

クラスAは次のことを行います。

  1. メモリのブロックを割り当てます
  2. その後、しばらくすると、このメモリブロックが削除されますが、ポインタはNULLに設定されません。

クラスBは次のことを行います

  1. メモリを割り当てます(この場合、クラスAによって削除されたのと同じメモリブロックが割り当てられます)。

この時点で、クラスAとクラスBの両方に同じメモリブロックを指すポインタがありますが、クラスAに関する限り、このメモリブロックは終了しているため存在しません。

次の問題を検討してください。

クラスAに論理エラーがあり、その結果、クラスBに属するメモリに書き込まれた場合はどうなりますか?

この特定のインスタンスでは、クラスAがクラスBのデータを事実上破壊している間、メモリアドレスは有効であるため、不良アクセス例外エラーは発生しません。

クラスBは、予期しない値に遭遇すると最終的にクラッシュする可能性があり、クラッシュした場合、問題がクラスAにあるときに、クラスBでこのバグの検出にかなりの時間を費やすことになります。

削除されたメモリポインタをNULLに設定した場合、クラスAの論理エラーがNULLポインタに書き込もうとするとすぐに例外エラーが発生します。

ポインタが2度目にNULLになったときに、二重削除による論理エラーが心配な場合は、これにアサートを追加します。

また、反対票を投じる場合は説明してください。


2
論理エラーがあった場合は、マスクするのではなく、修正する必要があります。
uınbɐɥs

@ Barmar、OPは言う... 6.削除後にポインターをNULLに設定-二重削除保護//悪くない 一部の人々はそれをNullに設定しないと応答しました、そして私はそれをNULLに設定する必要がある理由を言っています、6のどの部分がNULLを設定する私の答えは6に適合しませんか?
John

1
@Shaquin、そしてそもそもこれらの論理エラーを見つけるためにどのように提案しますか?メモリが削除された後にポインタ変数をNULLに設定すると、NULLポインタを参照しようとすると、不正な試みが行われた行でデバッガにクラッシュします。トレースして、論理エラーの場所を確認し、問題を修正できます。メモリを削除した後にポインタ変数をNULLに設定しない場合、UNAWAREロジックエラーが原因でこの削除されたメモリへの不正な書き込みが成功する可能性があるため、その時点でクラッシュしません。それをマスキングするものではありません。
John

1

常に中かっこを付けることは、非常にシンプルで堅牢なルールです。ただし、中括弧が多い場合、コードは洗練されていないように見えます。ルールで中括弧を省略できる場合は、より詳細なスタイルルールとより高度なツールが必要です。そうしないと、混乱して混乱する(エレガントではない)コードが簡単に発生する可能性があります。したがって、使用されている他のスタイルガイドやツールとは別の単一のスタイルルールを見ても効果がない可能性があります。他の回答では言及されていない、ルール3に関する重要な詳細をいくつか紹介します。

最初の興味深い詳細は、そのルールのほとんどの支持者がの場合にそれに違反することに同意することですelse。言い換えれば、彼らはレビューでそのようなコードを要求しません:

// pedantic rule #3
if ( command == Eat )
{
    eat();
}
else
{
    if ( command == Sleep )
    {
        sleep();
    }
    else
    {
        if ( command == Drink )
        {
            drink();
        }
        else
        {
            complain_about_unknown_command();
        }
    }
}

代わりに、彼らがそれを見る場合、彼らはそのようにそれを書くことさえ提案するかもしれません:

// not fully conforming to rule #3
if ( command == Eat )
{
    eat();
}
else if ( command == Sleep )
{
    sleep();
}
else if ( command == Drink )
{
    drink();
}
else
{
   complain_about_unknown_command();
}

elseとの間に中括弧がないため、これは技術的にはこの規則の違反ですif。このようなルールの二重性は、マインドレスなツールを使用してコードベースに自動的に適用しようとするときに表面化します。確かに、なぜ論争するのか、スタイルを自動的に適用するツールを使用してください。

2番目の詳細(これもそのルールの支持者によって忘れられることが多い)は、発生する可能性のあるエラーは、そのルール#3の違反のためだけではないということです。実際、それらはほとんど常にルール#1の違反を伴います(誰も議論しません)。自動ツールの観点からも、ルール#1に違反するとすぐに文句を言うツールを作成することは難しくないため、ほとんどのエラーをタイムリーにキャッチできます。

3番目の詳細(そのルールの反対者がしばしば忘れる)は、単一のセミコロンで表される空のステートメントの混乱する性質です。ある程度の経験を持つほとんどの開発者は、セミコロンを間違って配置したり、セミコロンを1つ使用して記述された空のステートメントによって遅かれ早かれ混乱したりしました。1つのセミコロンの代わりに2つの中括弧を使用すると、視覚的に見つけやすくなります。


1

常に{}1行で使用するわけではないことを認めなければなりませんが、それは良い習慣です。

  • 次のような括弧なしのコードを書いたとします。

    for(int i = 0; i <100; ++ i)for(int j = 0; j <100; ++ j)DoSingleStuff();

しばらくしてから、jループに他の要素を追加したいのですが、アラインメントでそれを行い、ブラケットを追加するのを忘れます。

  • メモリの割り当て解除が高速です。あなたは大きなスコープを持っていて、内部に大きな配列を作成するとしましょう(newそうしないとそれらはスタックになります)。これらの配列は、スコープを離れた直後にメモリから削除されます。ただし、その配列を1か所で使用すると、しばらくの間スタックになり、ある種のゴミになる可能性があります。スタックには制限があり、サイズが非常に小さいため、スタックサイズを超える可能性があります。ですから、それ{}を防ぐために書く方が良い場合もあります。注:これは1行ではなく、そのような状況用です。

    if(...){// SomeStuff ... {// if、whileなどはありません。// SomeOtherStuff} // SomeMoreStuff}

  • それを使用する3番目の方法は2番目と同様です。それは単にスタックをきれいにするためではなく、いくつかの関数を開くためです。mutex通常、長い関数で使用する場合は、データにアクセスする直前と、データの読み取り/書き込みを完了した直後に、ロックとロック解除を行うことをお勧めします。NOTEあなたには、いくつかの独自のを持っている場合は、この方法が使用されclassたりstructしてconstructordestructorロックメモリに。

  • さらに何が:

    if(...)if(...)SomeStuff(); else SomeOtherStuff(); // 2番目のifに移動しますが、最初はオンになっていることが示されます...

オールインオール、私は言うことはできません、常に{}単一の行に使用するための最良の方法は何ですか?それは悪いことではありません。

重要な編集 1行のコンパイルコードブ​​ラケットを記述しても何も起こりませんが、コードが解釈されると、コードの速度が非常に遅くなります。ほんの少し。


1

制御ステートメントを書く方法はいくつかあります。それらの特定の組み合わせは、読みやすさを損なうことなく共存できますが、他の組み合わせは問題を引き起こします。スタイル

if (condition)
  statement;

は、制御ステートメントを記述する他のいくつかの方法と快適に共存しますが、他の方法とはあまりうまく共存しません。複数行の制御ステートメントが次のように記述されている場合:

if (condition)
{
  statement;
  statement;
}

次に、どのifステートメントが単一行を制御し、どのステートメントが複数行を制御するかが視覚的に明らかになります。ただし、複数行のifステートメントが次のように記述されている場合:

if (condition) {
  statement;
  statement;
}

その場合、誰かifが必要な中括弧を追加せずに単一ステートメント構成を拡張しようとする可能性ははるかに高くなる可能性があります。

ifコードベースがフォームを大幅に使用する場合、次の単一ステートメントの次のステートメントも問題になる可能性があります。

if (condition) statement;

私自身の好みは、if同じ行にステートメントを置くことで、同様の制御ブロックを持つ多くのステートメントがある場合を除いて、一般的に読みやすさが向上することです。

if (x1 > xmax) x1 = xmax;
if (x1 < xmin) x1 = xmin;
if (x2 > xmax) x2 = xmax;
if (x2 < xmin) x2 = xmin;
etc.

その場合、一般的には、そのようなifステートメントのグループの前後に空白行を置いて、それらを他のコードから視覚的に分離します。すべてifが同じインデントで始まる一連のステートメントがあると、異常なことが明確に視覚的に示されます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.