私は私のC++講師の講義ノートを読んでおり、彼は次のように書いています。
- インデントを使用// OK
- 演算子の優先順位に依存しない-常に括弧を使用する// OK
- 常に{}ブロックを使用します-1行であっても// not OK、なぜ???
- 比較の左側のconstオブジェクト// OK
- 0以上の変数には符号なしを使用します//素敵なトリック
- 削除後にポインターを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++;
最初のバージョンを使用する利点は何ですか?
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();
ここで、cond1
が満たされていないときにdoSomethingElse()
にしたいとします(新機能)。そう:
if (cond1)
if (cond2)
doSomething();
else
doSomethingElse();
else
は内部のif
に関連付けられているため、明らかに間違っています。
編集:これはいくつかの注目を集めているので、私の見解を明確にします。私が答えていた質問は:
最初のバージョンを使用する利点は何ですか?
私が説明した。いくつかの利点があります。しかし、IMO、「常に」ルールが常に適用されるわけではありません。だから私は完全にサポートしていません
常に{}ブロックを使用します-たとえ1行であっても// // OKではない、なぜ???
alwaysは{}
ブロックを使用すると言っているわけではありません。それが単純で十分な条件と行動である場合、そうしないでください。後で誰かが来て、機能を追加するためにコードを変更する可能性があると思われる場合は、してください。
{
と}
を使用しない場合、コメントで誤って制御フローを変更するのは非常に簡単です。例えば:
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()
は常に呼び出されるとは限りません。
コードベースでこの問題が発生しました。リリース前に誰かが非常に迅速に一部の機能を無効にするために行っていました。幸いなことに、コードレビューでそれを見つけました。
講師の能力には疑問があります。彼のポイントを考慮して:
(b*b) - ((4*a)*c)
を本当に書く人(または読みたい人)いくつかの優先順位は明らか(またはそうあるべき)であり、余分な括弧は混乱を招くだけです。 (その一方で、括弧は必要ではないとわかっていても、それほど明白ではない場合には括弧を使用する必要があります。)if(cond){ code; }そして:
if(cond) { code; }最初に、私は彼に同意します。開口部
{
はそれほど見えないので、常にそこにあると仮定するのが最善です。ただし、2番目では、私(および私が一緒に仕事をしたことのあるほとんどの人)は、単一のステートメントの括弧を省略しても問題ありません。 (もちろん、インデントは体系的であり、このスタイルを一貫して使用することを提供します(また、非常に読みやすいコードを書いている多くの優秀なプログラマーは、最初の方法でフォーマットする場合でも中括弧を省略します)。if ( NULL == ptr )
のようなものは読みにくくなるほどugいです。比較を直観的に記述します。 (多くの場合、右の定数になります。)彼の4は悪いアドバイスです。コードが不自然になるものは、読みにくくなります。int
以外は特別な場合のために予約されています。経験豊富なCおよびC++プログラマにとって、unsigned
の使用はビット演算子を通知します。 C++には実際の基数型(または他の有効なサブレンジ型)はありません。プロモーションルールのため、unsigned
は数値に対して機能しません。シリアル番号のように、算術演算が意味をなさない数値は、おそらくunsigned
です。ただし、それは間違ったメッセージを送信するため、これに反対します。ビット単位の操作も意味をなしません。基本的なルールは、整数型がint
、_ unless_であるということです。別の型を使用する重要な理由があります。delete this;
が最も頻繁に発生するケースであり(this
をNULL
に設定することはできません)、そうでない場合はほとんどのdelete
はデストラクタ内にあるため、後でポインタにアクセスすることはできません。そして、それをNULL
に設定しても、他のポインターが動き回ることはありません。ポインタを体系的にNULL
に設定すると、誤った安心感を与え、実際には何も購入しません。典型的な参照のいずれかのコードを見てください。 Stroustrupは、たとえば、最初のルールを除き、指定したすべてのルールに違反します。
別の講師を見つけることをお勧めします。彼が話していることを実際に知っている人。
他のすべての答えは、講師のルール3を守ります。
私はあなたに同意すると言いましょう:ルールは冗長ですそして私はそれを勧めません。 理論的には、中括弧を常に追加するとエラーを防ぎます。一方、実生活でこの問題に遭遇したことはありません:他の答えが示唆することとは反対に、必要になったら中括弧を追加することを忘れていません。適切なインデントを使用すると、複数のステートメントがインデントされると、中括弧を追加する必要があることがすぐに明らかになります。
「コンポーネント10」による答えは、実際にこれが実際にエラーにつながる可能性がある唯一の考えられるケースを実際に強調しています。しかし、一方で、正規表現を介してコードを置き換えることは常にとにかく多大な注意を必要とします。
メダルの反対側を見てみましょう:常に中括弧を使用することに欠点がありますか?他の答えはこの点を単に無視します。しかし、そこにはis欠点があります:それは多くの垂直画面スペースを占有し、これはスクロールする必要があるためコードが読めなくなる可能性があります必要以上に。
最初に多くのガード句を持つ関数を考えてみてください(そして、はい、以下は悪い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番目のコードは肥大化しています。
そして、はい、これは前の行に開き中かっこを置くことである程度軽減できます。ただし、stillは、中括弧のないコードよりも読みにくくなります。
つまり、画面スペースを占有する不要なコードを記述しないでください。
私が取り組んでいるコードベースには、ブレースに対する病理学的な嫌悪感を持つ人々によってコードが散らばっています。後でやってくる人々にとっては、保守性に本当に違いをもたらす可能性があります。
私が遭遇した最も頻繁な問題のある例はこれです:
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;
したがって、私がやって来て、当時の文を追加したいとき、私が注意していなければ、私は簡単にこれで終わることができます:
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秒かかり、デバッグの混乱を最小限に抑えることができるとしたら、なぜnotを減らした曖昧性オプションを使用するのでしょうか?私には不経済のようです。
私の2c:
インデントを使用する
明らかに
演算子の優先順位に依存しない-常に括弧を使用する
「決して」「常に」という言葉は使いませんが、一般的にはこのルールが役に立つと思います。一部の言語(LISP、Smalltalk)では、これは問題ではありません。
常に{}ブロックを使用します-1行であっても
私はそれを一度もやったことも一度もありませんでしたが、それが学生にとってどのように良いのか、特にわかりました。以前にPythonを勉強した場合。
比較の左側のconstオブジェクト
依田条件?いいえ、お願いします。可読性が損なわれます。コードをコンパイルするときは、最大の警告レベルを使用してください。
0以上の変数には符号なしを使用します
OK。面白いことに、Stroustrupの意見が違うと聞いたことがあります。
削除後にポインターをNULLに設定-二重削除保護
悪いアドバイス!削除されたオブジェクトまたは存在しないオブジェクトを指すポインターを決して持たないでください。
より直感的でわかりやすくなっています。意図が明確になります。
また、新しいコードステートメントの追加中に、新しいユーザーが{
、}
を無意識のうちに見逃した場合に、コードが破損しないようにします。
上記の非常に賢明な提案に追加するために、これが重要になるコードをリファクタリングしているときに出会った一例は次のとおりです。非常に大きなコードベースを変更して、あるAPIから別のAPIに切り替えました。最初の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
が追加され、ブラケットがない場合にブラケットを追加できるようになりました。私と同じ位置。
私が考えることができる最も適切な例:
if(someCondition)
if(someOtherCondition)
DoSomething();
else
DoSomethingElse();
if
はどのelse
とペアになりますか?インデントは、外側のif
がelse
を取得することを意味しますが、実際にはコンパイラーがそれを見る方法ではありません。 innerif
はelse
を取得し、外側のif
は取得しません。このコードが期待どおりにならない理由を検査で把握するには、それを知る必要があります(または、デバッグモードでそのように動作することを確認する必要があります)。 Pythonを知っていると、さらに混乱します。その場合、インデントがコードブロックを定義することを知っているので、インデントに従って評価することが期待できます。ただし、C#では、空白については飛び回ることができません。
さて、そうは言っても、この「常にブラケットを使用する」という規則には特に同意しません。コードの垂直方向のノイズが非常に大きくなり、コードをすばやく読み通すことができなくなります。ステートメントが次の場合:
if(someCondition)
DoSomething();
...次に、このように記述する必要があります。 「常に括弧を使用する」という文は、「数学演算を常に括弧で囲む」ように聞こえます。それは非常に単純なステートメントa * b + c / d
を((a * b) + (c / d))
に変え、親括弧(多くのコーダーの悩みの種)が欠落する可能性を導入し、何のために?操作の順序はよく知られており、徹底されているため、括弧は冗長です。括弧を使用して、通常適用される操作とは異なる操作の順序を強制するだけです。たとえば、a * (b+c) / d
です。ブロックブレースは似ています。これらを使用して、デフォルトとは異なり、「自明」ではない場合(主観的ですが、通常はかなり常識的)に行うことを定義します。
明らかないくつかの場合を除いて、どこでも{}
を使用しています。単一行は、ケースの1つです。
if(condition) return; // OK
if(condition) //
return; // and this is not a one-liner
戻る前にメソッドを追加すると、あなたを傷つける可能性があります。インデントは、条件が満たされたときに戻りが実行されていることを示しますが、常に戻ります。
Statmentを使用した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();
}
}
答えを見てみると、誰も私が習慣にしている習慣を明示的に述べておらず、あなたのコードの物語を語っています:
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++
をsame lineに入れて、ifが他の人に信号を送るように、"このブロックにj
だけを増分させたい。もちろん、これは、行が可能な限り単純化されている場合にのみ価値があります。これは、 peri が示すように、ここにブレークポイントを置くことはあまり有用ではないからです。
実際、Javaでこのようなコードを備えたTwitter Storm APIの一部を実行したばかりです。実行コードの関連スニペットを このスライドショーの43ページ に示します。
...
Integer Count = counts.get(Word);
if (Count=null) count=0;
count++
...
Forループブロックには2つのことが含まれているため、そのコードをインライン化しません。つまりnever:
int j = 0;
for (int i = 0 ; i < 100 ; ++i) if (i % 2 == 0) j++;
それはひどく、私はそれが(意図したように)動作するかどうかさえ知りません。 これをしないでください。新しい行と中括弧は、散文でコンマまたはセミコロンが行うのと同じ方法で、別々の関連するコードを区別するのに役立ちます。上記のブロックは、いくつかの節と、別々の部分を区別するために中断したり一時停止したりしないその他のステートメントを含む、本当に長い文と同じくらい悪いです。
本当に他の人に電信したい場合は、三項演算子または ?:
形式を使用する1行のみのジョブです。
for (int i = 0 ; i < 100 ; ++i) (i%2 ? 0 : >0) j++;
しかし、これはcode-golfに迫っていて、素晴らしいプラクティスではないと思います(j ++を:
の片側に置くべきかどうかは明確ではありません)。 NB以前にC++で三項演算子を実行したことがない、これが機能するかどうかわからない、しかし、それは存在します。
読者(つまり、コードを保守している人)がストーリー(コード)をどのように解釈するか想像してみてください。できる限り明確にしてください。初心者のコーダー/生徒がこれを維持していることがわかっている場合は、混乱しないように、できるだけ多くの{}
を残してください。
{}
のない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
が常に失敗する理由を尋ねる)。
しかし、それは私が{}
をどこにでも追加することを意味するわけではありません(私の意見では、{}
をどこでも見るのは面倒です)。それは問題を引き起こす可能性がありますが、私の個人的なコーディングスタイルは、{}
のない条件が同じ行にないときに条件を禁止しているため、私自身のプロジェクトには決して影響しません(はい、私のコーディングスタイルは型にはまらないことに同意しますが、他のプロジェクトに貢献するときは、プロジェクトのコードスタイルを使用します)。これにより、次のコードが正常になります。
if (something) goto fail;
しかし、次のものではありません。
if (something)
goto fail;
wrt 6:nullポインターの削除は何もしないので安全です。そのため、偶然そのパスを2回通過しても、空きメモリまたは他の何かに割り当てられたメモリを解放することでメモリが破損することはありません。
これは、静的ファイルスコープオブジェクトと、ライフタイムがあまり明確ではなく、破棄された後に再作成されることが知られているシングルトンに関する問題のほとんどです。
ほとんどの場合、auto_ptrsを使用してこの必要性を回避できます。
上記で説明したエラーの防止に役立つオプションの1つは、ブレースを使用しない場合に発生することをインライン化することです。コードを変更しようとすると、エラーに気付かないことが非常に難しくなります。
if (condition) doSomething();
else doSomethingElse();
if (condition) doSomething();
doSomething2(); // Looks pretty obviously wrong
else // doSomethingElse(); also looks pretty obviously wrong
私はLuchianの受け入れられた答えが好きです、実際、彼が正しいという難しい方法を学んだので、私は常に単一行のブロックであってもブレースを使用します。しかし、あなたがあなたの例にあるように、個人的に私はフィルターを書くときに例外を作ります。この:
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
クロージャを持たない言語では、フィルタを単一のステートメントで表現することはできませんが、ifステートメントが後に続くforループでなければなりません。ただし、それはプログラマの心の中にある1つのアクションであり、次のようにコードに反映する必要があると思います。
int j = 0;
for (int i = 0 ; i < 100 ; ++i)
if (i % 2 == 0)
{
j++;
}
ループと条件ブロックのスコープを明確に定義することにより、コードが読みやすくなります。また、偶然の間違いからあなたを救います。
中括弧を追加する別の例。バグを探していて、そのようなコードを見つけたら:
void SomeSimpleEventHandler()
{
SomeStatementAtTheBeginningNumber1;
if (conditionX) SomeRegularStatement;
SomeStatementAtTheBeginningNumber2;
SomeStatementAtTheBeginningNumber3;
if (!SomeConditionIsMet()) return;
OtherwiseSomeAdditionalStatement1;
OtherwiseSomeAdditionalStatement2;
OtherwiseSomeAdditionalStatement3;
}
メソッドを1行ずつ読んでいくと、メソッドに条件があり、それがtrueでない場合に戻ります。しかし実際には、いくつかの条件に基づいていくつかの変数を設定する他の100個の単純なイベントハンドラのように見えます。そしてある日、Fast Coderが登場し、メソッドの最後に変数設定ステートメントを追加します。
{
...
OtherwiseSomeAdditionalStatement3;
SetAnotherVariableUnconditionnaly;
}
その結果、SomeConditionIsMet()のときにSetAnotherVariableUnconditionnalyが実行されますが、すべての行のサイズがほぼ同じであり、戻り条件が垂直方向にインデントされている場合でも、気付かないほど速い人は気づきませんでした。
条件付きリターンが次のようにフォーマットされている場合:
if (!SomeConditionIsMet())
{
return;
}
それは非常に顕著であり、Fast Coderは一目でそれを見つけます。
あなたがコンパイラーであれば、違いはありません。両方とも同じです。
しかし、プログラマにとっては、最初のものはより明確で読みやすく、エラーが発生しにくいものです。
常に単一行に{}
を使用するとは限りませんが、それは良い練習です。
次のように括弧なしでコードを書くとしましょう:
for(int i = 0; i <100; ++ i)for(int j = 0; j <100; ++ j)DoSingleStuff();
そして、しばらくしてからj
ループに他の要素を追加したいので、それをアライメントで行い、括弧の追加を忘れます。
メモリの割り当て解除は高速です。大きなスコープを持ち、内部に大きな配列を作成するとしましょう(new
なしでスタックになります)。これらの配列は、スコープを離れた直後にメモリから削除されます。ただし、その配列を1か所で使用すると、しばらくの間スタックになり、何らかのゴミになる可能性があります。スタックには制限があり、非常に小さいサイズであるため、スタックサイズを超える可能性があります。そのため、場合によっては{}
を記述してそれを防ぐ方が良い場合があります。 NOTEこれは単一行用ではなく、そのような状況用です:
if(...){// SomeStuff ... {// if、whileなどがない// SomeOtherStuff} // SomeMoreStuff}
3番目の使用方法は2番目の使用方法と似ています。単にスタックをきれいにするのではなく、いくつかの機能をopenにします。長い関数でmutex
を使用する場合、通常、データにアクセスする直前、および読み取り/書き込みが終了した直後にロックおよびロック解除することをお勧めします。 NOTEこの方法は、独自のclass
またはstruct
とconstructor
およびdestructor
を使用してメモリをロックする場合に使用します。
さらに何ですか:
if(...)if(...)SomeStuff();それ以外の場合SomeOtherStuff(); // 2番目のifに移動しますが、alligmentはそれが最初にあることを示します...
All In Allでは、常に1行に{}
を使用する最良の方法は何と言ってもいいわけではありませんが、そうするのは悪くありません。
重要な編集 1行のコードブラケットをコンパイルする場合は何も行いませんが、コードが解釈される場合、コードの速度が非常にわずかに低下します。ほんの少し。
常に中括弧を使用することは、非常にシンプルで堅牢なルールです。ただし、中かっこが多い場合、コードは見栄えがよくない場合があります。規則で中括弧を省略できる場合は、より詳細なスタイル規則とより洗練されたツールが必要です。そうしないと、混chaとした混乱した(エレガントではない)コードが簡単に発生する可能性があります。したがって、他のスタイルガイドやツールとは別の単一のスタイルルールを使用するのは無意味です。他の回答でも言及されていない、ルール#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番目の詳細(多くの場合、このルールの反対者によって忘れられています)は、単一のセミコロンで表される空のステートメントの混乱する性質です。経験のある開発者のほとんどは、セミコロンの置き忘れや、セミコロンのみを使用して記述された空のステートメントによって、遅かれ早かれ混乱してしまいました。単一のセミコロンの代わりに2つの中括弧を使用すると、視覚的に簡単に見つけられます。
完了したら、ポインタをNULLに設定することをお勧めします。
理由の例を次に示します。
クラスAは次のことを行います。
クラスBは次のことを行います
この時点で、クラスAとクラスBの両方に同じメモリブロックを指すポインタがあります。クラスAに関する限り、このメモリブロックは終了しているため存在しません。
次の問題を考慮してください。
クラスAで論理エラーが発生し、クラスBに属するメモリに書き込みが発生した場合はどうなりますか?
この特定のインスタンスでは、メモリアドレスが正当であるため、不正アクセス例外エラーは発生しませんが、クラスAはクラスBデータを事実上破損しています。
クラスBは、予期しない値に遭遇すると最終的にクラッシュする可能性があり、クラッシュすると、問題がクラスAにあるときにクラスBでこのバグを探し出すのにかなりの時間を費やす可能性があります。
削除されたメモリポインターをNULLに設定した場合、クラスAの論理エラーがNULLポインターに書き込もうとするとすぐに例外エラーが発生します。
ポインターが2回目にNULLのときに二重削除で論理エラーが心配な場合は、このためにアサートを追加します。
また、反対票を投じる場合は、説明してください。
制御ステートメントを作成する方法はいくつかあります。それらの特定の組み合わせは、読みやすさを損なうことなく共存できますが、他の組み合わせは問題を引き起こします。スタイル
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
で始まる一連のステートメントを使用すると、何か異常なものがあることを明確に視覚的に示すことができます。