it-swarm.com.de

Was ist der Zweck der Verwendung von geschweiften Klammern (d. H. {}) Für ein einzeiliges if oder eine Schleife?

Ich lese einige Vorlesungsunterlagen meines C++ - Dozenten und er schrieb Folgendes:

  1. Einrückung verwenden // OK
  2. Verlassen Sie sich niemals auf die Priorität des Bedieners - Verwenden Sie immer Klammern // OK
  3. Verwenden Sie immer einen {} Block - auch für eine einzelne Zeile // nicht OK, warum ???
  4. Const-Objekt auf der linken Seite des Vergleichs // OK
  5. Verwenden Sie unsigned für Variablen, die> = 0 // Nice trick sind
  6. Zeiger nach dem Löschen auf NULL setzen - doppelter Löschschutz // nicht schlecht

Die 3. Technik ist mir nicht klar: Was würde ich gewinnen, wenn ich eine Zeile in einem { ... } Platziere?

Nehmen Sie zum Beispiel diesen seltsamen Code:

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

und ersetze es durch:

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

Was ist der Vorteil der 1. Version?

312
JAN

Versuchen wir, auch i zu ändern, wenn wir j erhöhen:

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

Ach nein! Aus Python kommend sieht das in Ordnung aus, ist es aber nicht, da es äquivalent ist zu:

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

Natürlich ist dies ein dummer Fehler, den selbst ein erfahrener Programmierer machen könnte.

Ein weiterer sehr guter Grund ist in Antwort von ta.speot.is angegeben.

Ein drittes , an das ich denken kann, ist geschachteltes if:

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

Angenommen, Sie möchten jetzt doSomethingElse(), wenn cond1 Nicht erfüllt ist (neue Funktion). So:

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

was offensichtlich falsch ist, da das else mit dem inneren if assoziiert.


Bearbeiten: Da dies einige Aufmerksamkeit erhält, werde ich meine Ansicht klären. Die Frage, die ich beantwortete, ist:

Was ist der Vorteil der 1. Version?

Was ich beschrieben habe. Es gibt einige Vorteile. Aber IMO, "immer" Regeln gelten nicht immer. Ich unterstütze das also nicht ganz

Verwenden Sie immer einen {} Block - auch für eine einzelne Zeile // nicht OK, warum ???

Ich sage nicht immer Verwenden Sie einen {} - Block. Wenn es einfach genug ist, tun Sie es nicht. Wenn Sie den Verdacht haben, dass später jemand hereinkommt und Ihren Code ändert, um Funktionen hinzuzufügen, tun Sie Folgendes.

505
Luchian Grigore

Es ist sehr einfach, versehentlich den Kontrollfluss mit Kommentaren zu ändern, wenn Sie nicht { Und } Verwenden. Beispielsweise:

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

must_always_do_this();

Wenn Sie do_something_else() mit einem einzeiligen Kommentar auskommentieren, erhalten Sie Folgendes:

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

must_always_do_this();

Es wird kompiliert, aber must_always_do_this() wird nicht immer aufgerufen.

Wir hatten dieses Problem in unserer Codebasis, wo sich jemand gemeldet hatte, um einige Funktionen sehr schnell vor der Veröffentlichung zu deaktivieren. Zum Glück haben wir es in der Codeüberprüfung erwischt.

318
ta.speot.is

Ich habe meine Zweifel an der Kompetenz des Dozenten. In Anbetracht seiner Punkte:

  1. Okay
  2. Würde jemand wirklich schreiben (oder lesen wollen) (b*b) - ((4*a)*c)? Einige Präzedenzfälle liegen auf der Hand (oder sollten es auch sein), und die zusätzlichen Klammern tragen nur zur Verwirrung bei. (Andererseits sollten Sie die Klammern in weniger offensichtlichen Fällen verwenden, selbst wenn Sie wissen, dass sie nicht benötigt werden.)
  3. Art von. Es gibt zwei weit verbreitete Konventionen zum Formatieren von Bedingungen und Schleifen:
     if (cond) {
     code; 
    } 
    
    und:
     if (cond) 
     {
     code; 
    } 
    
    Im ersten Fall stimme ich ihm zu. Die Öffnung { Ist nicht so sichtbar, also ist es am besten anzunehmen, dass sie immer da ist. Im zweiten Fall habe ich (und die meisten Leute, mit denen ich gearbeitet habe) kein Problem damit, die geschweiften Klammern für eine einzelne Aussage wegzulassen. (Vorausgesetzt natürlich, dass die Einrückung systematisch ist und Sie diesen Stil konsistent verwenden. (Und viele sehr gute Programmierer, die gut lesbaren Code schreiben, lassen die geschweiften Klammern auch dann weg, wenn sie den ersten Weg einschlagen.)
  4. NEIN. Dinge wie if ( NULL == ptr ) sind hässlich genug, um die Lesbarkeit zu beeinträchtigen. Schreiben Sie die Vergleiche intuitiv. (Was in vielen Fällen zu einer Konstante auf der rechten Seite führt.) Seine 4 ist ein schlechter Rat. Alles, was den Code unnatürlich macht, macht ihn weniger lesbar.
  5. NEIN. Alles andere als int ist für Sonderfälle reserviert. Für erfahrene C- und C++ - Programmierer signalisiert die Verwendung von unsigned Bitoperatoren. C++ hat keinen echten Kardinaltyp (oder einen anderen effektiven Unterbereichstyp). unsigned funktioniert aufgrund der Heraufstufungsregeln nicht für numerische Werte. Numerische Werte, für die keine arithmetischen Operationen sinnvoll wären, wie Seriennummern, könnten vermutlich unsigned sein. Ich würde jedoch dagegen argumentieren, weil es die falsche Nachricht sendet: Bitweise Operationen sind ebenfalls nicht sinnvoll. Die Grundregel lautet, dass ganzzahlige Typen int sind, _unabhängig_ es einen wichtigen Grund für die Verwendung eines anderen Typs gibt.
  6. NEIN. Dies systematisch zu tun ist irreführend und schützt eigentlich nicht vor irgendetwas. In striktem OO Code, delete this; Ist oft der häufigste Fall (und Sie können this nicht auf NULL setzen), und ansonsten Die meisten delete befinden sich in Destruktoren, sodass Sie später sowieso nicht auf den Zeiger zugreifen können. Wenn Sie NULL festlegen, werden keine anderen Zeiger geändert NULL gibt ein falsches Sicherheitsgefühl und kauft dir nichts.

Sehen Sie sich den Code in einer der typischen Referenzen an. Stroustrup verstößt beispielsweise gegen jede Regel, die Sie angegeben haben, mit Ausnahme der ersten.

Ich würde vorschlagen, dass Sie einen anderen Dozenten finden. Einer, der tatsächlich weiß, wovon er spricht.

60
James Kanze

Alle anderen Antworten verteidigen die Regel 3 Ihres Dozenten.

Lassen Sie mich sagen, dass ich Ihnen zustimme: die Regel ist überflüssig und ich würde es nicht empfehlen. Es ist wahr, dass es theoretisch Fehler verhindert, wenn Sie immer geschweifte Klammern hinzufügen. Auf der anderen Seite ich bin im wirklichen Leben noch nie auf dieses Problem gestoßen: Im Gegensatz zu den anderen Antworten habe ich nicht einmal vergessen, die geschweiften Klammern einzufügen, sobald sie notwendig wurden. Wenn Sie die richtige Einrückung verwenden, wird sofort klar, dass Sie geschweifte Klammern hinzufügen müssen, sobald mehr als eine Anweisung eingerückt ist.

Die Antwort von „Component 10“ zeigt tatsächlich den einzig denkbaren Fall auf, in dem dies tatsächlich zu einem Fehler führen könnte. Andererseits erfordert das Ersetzen von Code durch reguläre Ausdrücke immer enorme Sorgfalt.

Schauen wir uns nun die andere Seite der Medaille an: Gibt es einen Nachteil immer geschweifte Klammern zu verwenden? Die anderen Antworten ignorieren diesen Punkt einfach. Aber es gibt ist einen Nachteil: Es nimmt viel vertikalen Bildschirmplatz ein, und dies kann wiederum Ihren Code unleserlich machen, da Sie mehr als nötig scrollen müssen.

Betrachten Sie eine Funktion mit vielen Schutzklauseln am Anfang (und ja, das Folgende ist schlechter C++ - Code, aber in anderen Sprachen wäre dies eine recht häufige Situation):

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);
}

Dies ist ein schrecklicher Code, und ich behaupte nachdrücklich, dass Folgendes weitaus besser lesbar ist:

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);
}

In ähnlicher Weise profitieren kurze verschachtelte Schleifen davon, dass die geschweiften Klammern weggelassen werden:

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;
}

Vergleichen mit:

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;
}

Der erste Code ist prägnant; Der zweite Code ist aufgebläht.

Und ja, dies kann bis zu einem gewissen Grad gemildert werden, indem die öffnende Klammer in die vorherige Zeile gesetzt wird. Aber das wäre immernoch weniger lesbar als der Code ohne geschweifte Klammern.

Kurz gesagt: Schreiben Sie keinen unnötigen Code, der Platz auf dem Bildschirm beansprucht.

46
Konrad Rudolph

Die Codebasis, an der ich arbeite, wird von Leuten mit einer pathologischen Abneigung gegen Klammern mit Code übersät, und für die Leute, die später mitkommen, kann dies wirklich einen Unterschied für die Wartbarkeit bedeuten.

Das häufigste problematische Beispiel, auf das ich gestoßen bin, ist das folgende:

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;

Wenn ich also mitkomme und eine then-Anweisung hinzufügen möchte, kann ich leicht dazu kommen, wenn ich nicht aufpasse:

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;
}

Angesichts der Tatsache, dass das Hinzufügen von geschweiften Klammern ~ 1 Sekunde dauert und Sie mindestens ein paar verwirrende Minuten beim Debuggen sparen können, warum sollten Sie jemals nicht die Option mit reduzierter Mehrdeutigkeit wählen? Scheint mir eine falsche Wirtschaftlichkeit zu sein.

39
jam

Mein 2c:

Einrückung verwenden

Offensichtlich

Verlassen Sie sich niemals auf die Priorität des Bedieners - Verwenden Sie immer runde Klammern

Ich würde keine Wörter "nie und" immer "verwenden, aber im Allgemeinen sehe ich diese Regel als nützlich an. In einigen Sprachen (LISP, Smalltalk) ist dies kein Problem.

Verwenden Sie immer einen {} Block - auch für eine einzelne Zeile

Ich mache das nie und hatte nie ein einziges Problem, aber ich kann sehen, wie gut es für Studenten sein kann, besonders. wenn sie Python vorher studiert haben.

Const-Objekt auf der linken Seite des Vergleichs

Yoda Bedingungen? Nein, bitte. Es schadet der Lesbarkeit. Verwenden Sie einfach die maximale Warnstufe, wenn Sie Ihren Code kompilieren.

Verwenden Sie unsigned für Variablen, die> = 0 sind

OKAY. Komischerweise habe ich gehört, dass Stroustrup anderer Meinung ist.

Setzen Sie den Zeiger nach dem Löschen auf NULL - Doppelter Löschschutz

Schlechter Rat! Verfügen Sie niemals über einen Zeiger, der auf ein gelöschtes oder nicht vorhandenes Objekt verweist.

19

es ist intuitiver und leicht verständlich. Es macht die Absicht klar.

Und es stellt sicher, dass der Code nicht beschädigt wird, wenn ein neuer Benutzer beim Hinzufügen einer neuen Code-Anweisung unwissentlich die Zeichen { Und } Verpasst.

17
Alok Save

Als Ergänzung zu den oben genannten sehr sinnvollen Vorschlägen habe ich beim Umgestalten eines Codes, bei dem dies kritisch wird, Folgendes festgestellt: Ich habe eine sehr große Codebasis geändert, um von einer API auf eine andere zu wechseln. Die erste API hatte einen Aufruf zum Festlegen der Firmen-ID wie folgt:

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

für die Ersetzung waren zwei Anrufe erforderlich:

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

Ich habe mich daran gemacht, dies mit regulären Ausdrücken zu ändern, was sehr erfolgreich war. Wir haben den Code auch durch astyle geleitet, wodurch er wirklich viel besser lesbar wurde. Während des Überprüfungsprozesses stellte ich fest, dass sich dies unter bestimmten Umständen änderte:

if ( condition )
   setCompIds( compId, compSubId );

Dazu:

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

das ist eindeutig nicht das was benötigt wurde. Ich musste zum Anfang zurückkehren, um dies erneut zu tun, indem ich die Ersetzung als vollständig innerhalb eines Blocks behandelte und dann manuell alles änderte, was am Ende doof aussah (zumindest wäre es nicht falsch).

Mir ist aufgefallen, dass astyle jetzt die Option --add-brackets Hat, mit der Sie Klammern hinzufügen können, in denen es keine gibt, und ich empfehle dies nachdrücklich, wenn Sie sich jemals finden in der gleichen Position wie ich war.

13
Component 10

Das zutreffendste Beispiel, an das ich denken kann:

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

Mit welchem ​​if wird das else gepaart? Einrückung impliziert, dass das äußere if das else erhält, aber der Compiler sieht es nicht so. Das innereif erhält das else, das äußere if nicht. Sie müssen dies wissen (oder feststellen, dass es sich im Debugging-Modus so verhält), um durch eine Überprüfung herauszufinden, warum dieser Code möglicherweise Ihren Erwartungen entspricht. Es wird verwirrender, wenn Sie Python kennen. In diesem Fall wissen Sie, dass Einrückungen Codeblöcke definieren, und erwarten daher, dass sie entsprechend der Einrückung ausgewertet werden. C # gibt jedoch keinen fliegenden Flip über Whitespace.

Nun, das heißt, ich bin nicht besonders einverstanden mit dieser Regel "immer Klammern verwenden" im Gesicht. Dadurch wird der Code vertikal sehr verrauscht und die Fähigkeit, ihn schnell zu lesen, wird verringert. Wenn die Aussage lautet:

if(someCondition)
   DoSomething();

... dann sollte es so geschrieben sein. Die Aussage "immer Klammern verwenden" klingt wie "mathematische Operationen immer mit Klammern umgeben". Das würde die sehr einfache Anweisung a * b + c / d In ((a * b) + (c / d)) Umwandeln und die Möglichkeit einführen, dass ein enger Paren (der Fluch vieler Programmierer) fehlt, und wofür? Die Reihenfolge der Operationen ist bekannt und wird gut durchgesetzt, sodass die Klammern überflüssig sind. Sie würden nur Klammern verwenden, um eine andere Reihenfolge von Operationen zu erzwingen, als normalerweise angewendet würde: a * (b+c) / d zum Beispiel. Blockklammern sind ähnlich; Verwenden Sie sie, um zu definieren, was Sie in Fällen tun möchten, in denen es sich von der Standardeinstellung unterscheidet und nicht "offensichtlich" ist (subjektiv, aber normalerweise ziemlich vernünftig).

8
KeithS

Ich benutze {} überall mit Ausnahme einiger Fälle, in denen es offensichtlich ist. Einzelne Zeile ist einer der Fälle:

if(condition) return; // OK

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

Es kann Sie verletzen, wenn Sie eine Methode vor der Rückkehr hinzufügen. Einrückung zeigt an, dass die Rückgabe ausgeführt wird, wenn die Bedingung erfüllt ist, sie jedoch immer zurückgibt.

Anderes Beispiel in C # mit Anweisung

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

das ist äquivalent zu

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

Wenn Sie die Antworten durchsehen, hat niemand ausdrücklich die Art von Übung angegeben, die ich mir angewöhnt habe und die die Geschichte Ihres Codes erzählt:

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

Wird:

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

Setzen Sie den j++ In die selbe Zeile, wie das if an andere signalisieren soll. "Ich möchte nur, dass dieser Block jemals j erhöht." . Natürlich lohnt sich dies nur, wenn die Zeile so einfach wie möglich ist, da das Setzen eines Haltepunkts hier, wie in peri erwähnt, nicht sehr nützlich ist.

Tatsächlich bin ich gerade auf einen Teil der Twitter Storm-API gestoßen, der diese Art von Code in Java enthält. Hier ist der relevante Ausschnitt aus dem Ausführungscode auf Seite 43 dieser Diashow :

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

Der for-Schleifenblock enthält zwei Dinge, daher würde ich diesen Code nicht einbetten. Dh niemals :

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

Es ist schrecklich und ich weiß nicht einmal, ob es funktioniert (wie beabsichtigt); Mach das nicht . Neue Linien und geschweifte Klammern helfen dabei, getrennte, aber verwandte Codeteile zu unterscheiden, genau wie ein Komma oder ein Semikolon in der Prosa. Der obige Block ist ein wirklich langer Satz mit ein paar Sätzen und einigen anderen Anweisungen, die niemals unterbrochen oder unterbrochen werden, um einzelne Teile zu unterscheiden.

Wenn Sie wirklich an eine andere Person telegrafieren möchten, verwenden Sie einen ternären Operator oder ?: form:

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

Aber das ist fast Code-Golf, und ich denke nicht, dass es eine gute Übung ist (mir ist nicht klar, ob ich das j ++ auf eine Seite des : Setzen soll oder nicht). [~ # ~] nb [~ # ~] Ich habe vorher noch keinen ternären Operator in C++ ausgeführt. Ich weiß nicht, ob dies funktioniert. aber es existiert.

Zusamenfassend:

Stellen Sie sich vor, wie Ihr Leser (d. H. Die Person, die den Code verwaltet) Ihre Geschichte (den Code) interpretiert. Machen Sie es ihnen so klar wie möglich. Wenn Sie wissen, dass der neue Programmierer/Schüler dies beibehält, lassen Sie vielleicht sogar so viele {} Wie möglich ein, damit sie nicht verwirrt werden.

5
Pureferret

Denn wenn Sie zwei Anweisungen ohne {} Haben, können Sie leicht ein Problem übersehen. Nehmen wir an, der Code sieht so aus.

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;

Es sieht gut aus. Das Problem ist sehr leicht zu übersehen, besonders wenn die Funktion, die den Code enthält, viel größer ist. Das Problem ist, dass goto fail Bedingungslos ausgeführt wird. Sie können sich leicht vorstellen, wie frustrierend dies ist (Sie fragen sich, warum das letzte hash_update Immer fehlschlägt, schließlich sieht in der Funktion hash_update Alles gut aus).

Dies bedeutet jedoch nicht, dass ich überall {} Hinzufügen soll (meiner Meinung nach ist es ärgerlich, {} Überall zu sehen). Dies kann zwar Probleme verursachen, hat sich aber bei meinen eigenen Projekten nie bewährt, da mein persönlicher Codierungsstil Bedingungen ohne {} Verbietet, wenn sie nicht in derselben Zeile stehen (ja, ich stimme zu, dass mein Codierungsstil unkonventionell ist, aber Ich mag es und verwende den Code-Stil des Projekts, wenn ich zu anderen Projekten beitrage. Dies macht den folgenden Code in Ordnung.

if (something) goto fail;

Aber nicht der folgende.

if (something)
    goto fail;
5
Konrad Borowski

wrt 6: Es ist sicherer, weil das Löschen eines Nullzeigers ein No-Op ist. Wenn Sie diesen Pfad also zufällig zweimal durchlaufen, führt dies nicht zu einer Speicherbeschädigung, da Speicher freigegeben wird, der entweder frei ist oder einem anderen Zweck zugewiesen wurde.

Dies ist das häufigste Problem bei Objekten und Singletons mit statischem Dateibereich, bei denen die Lebensdauer nicht eindeutig ist und bei denen bekannt ist, dass sie nach ihrer Zerstörung neu erstellt werden.

In den meisten Fällen können Sie dies mit auto_ptrs vermeiden

4
Tom Tanner

Eine Möglichkeit, um die oben beschriebenen Fehler zu vermeiden, besteht darin, festzulegen, was passieren soll, wenn Sie keine geschweiften Klammern verwenden. Wenn Sie versuchen, den Code zu ändern, wird es wesentlich schwieriger, die Fehler nicht zu bemerken.

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

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

Ich mag Luchians akzeptierte Antwort. Tatsächlich habe ich auf die harte Tour gelernt, dass er Recht hat. Deshalb verwende ich immer geschweifte Klammern, auch für einzeilige Blöcke. Ich persönlich mache jedoch eine Ausnahme, wenn ich einen Filter schreibe, wie Sie es in Ihrem Beispiel sehen. Dies:

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

sieht für mich unübersichtlich aus. Es trennt die for-Schleife und die if-Anweisung in separate Aktionen, wenn Ihre Absicht wirklich eine einzelne Aktion ist: Um alle durch 2 teilbaren Ganzzahlen zu zählen.

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

In Sprachen ohne Abschlüsse kann der Filter nicht in einer einzelnen Anweisung ausgedrückt werden, sondern muss eine for-Schleife gefolgt von einer if-Anweisung sein. Es ist jedoch immer noch eine Aktion im Kopf des Programmierers, und ich glaube, das sollte sich im Code wie folgt widerspiegeln:

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

Es macht Ihren Code lesbarer, indem es den Umfang Ihrer Schleifen und bedingten Blöcke klar definiert. Es bewahrt Sie auch vor versehentlichen Fehlern.

4
Drona

Ein weiteres Beispiel für das Hinzufügen von geschweiften Klammern. Einmal suchte ich nach einem Fehler und fand solchen Code:

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

Wenn Sie die Methode Zeile für Zeile lesen, werden Sie feststellen, dass die zurückgegebene Methode eine Bedingung enthält, wenn sie nicht wahr ist. Tatsächlich sieht es aber so aus, als ob 100 andere einfache Ereignishandler Variablen basierend auf bestimmten Bedingungen festlegen. Und eines Tages kommt der Fast Coder und fügt am Ende der Methode eine zusätzliche Anweisung zum Festlegen von Variablen hinzu:

{
    ...
    OtherwiseSomeAdditionalStatement3;
    SetAnotherVariableUnconditionnaly;
}

Infolgedessen wird SetAnotherVariableUnconditionnaly ausgeführt, wenn SomeConditionIsMet () verwendet wird. Der schnelle Typ hat dies jedoch nicht bemerkt, da alle Zeilen fast gleich groß sind und selbst wenn die Rückgabebedingung vertikal eingerückt ist, ist dies nicht so auffällig.

Wenn die bedingte Rückgabe folgendermaßen formatiert ist:

if (!SomeConditionIsMet())
{
    return;
}

es ist sehr auffällig und der Fast Coder wird es auf einen Blick finden.

3
Artemix

Wenn Sie ein Compiler sind, macht es keinen Unterschied. Beide sind gleich.

Für Programmierer ist die erste jedoch klarer, einfacher zu lesen und weniger fehleranfällig.

3
P.P.

Ich halte den ersten für klar, dann den zweiten. Es gibt das Gefühl, Anweisungen zu schließen, mit wenig Code ist in Ordnung, wenn Code komplex wird. {...} Hilft sehr, auch wenn es endif oder begin...end Ist.

//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++;
2
RollRoll

Ich muss zugeben, dass nicht immer {} Für eine einzelne Zeile verwendet wird, aber es ist eine gute Praxis.

  • Nehmen wir an, Sie schreiben einen Code ohne Klammern, der so aussieht:

    für (int i = 0; i <100; ++ i) für (int j = 0; j <100; ++ j) DoSingleStuff ();

Und nach einiger Zeit möchten Sie in der j - Schleife ein paar andere Dinge hinzufügen, und Sie tun dies einfach durch Ausrichtung und vergessen, Klammern hinzuzufügen.

  • Die Speicherfreigabe erfolgt schneller. Nehmen wir an, Sie haben einen großen Bereich und erstellen große Arrays im Inneren (ohne new, damit sie im Stapel sind). Diese Arrays werden unmittelbar nach dem Verlassen des Gültigkeitsbereichs aus dem Speicher entfernt. Aber es ist möglich, dass Sie dieses Array an einem Ort verwenden, und es wird eine Weile gestapelt sein und eine Art Müll sein. Da ein Stapel eine begrenzte und ziemlich kleine Größe hat, ist es möglich, die Stapelgröße zu überschreiten. In einigen Fällen ist es daher besser, {} Zu schreiben, um dies zu verhindern. NOTE Dies gilt nicht für einzelne Zeilen, sondern für solche Situationen:

    if (...) {// SomeStuff ... {// wir haben kein if, while usw. // SomeOtherStuff} // SomeMoreStuff}

  • Der dritte Weg, es zu benutzen, ist ähnlich wie der zweite. Es geht nur nicht darum, den Stapel sauberer zu machen, sondern um einige Funktionen zu öffnen . Wenn Sie in langen Funktionen mutex verwenden, ist es in der Regel besser, das Sperren und Entsperren unmittelbar vor dem Zugriff auf Daten und unmittelbar nach dem Lesen/Schreiben durchzuführen. NOTE Diese Methode wird verwendet, wenn Sie eigene class oder struct mit constructor und destructor, um den Speicher zu sperren.

  • Was ist mehr:

    if (...) if (...) SomeStuff (); else SomeOtherStuff (); // geht zum zweiten wenn, aber das Alligment zeigt, dass es auf dem ersten ist ...

Alles in allem kann ich nicht sagen, wie man am besten immer {} Für eine einzelne Zeile verwendet, aber es ist nichts Schlimmes, das zu tun.

WICHTIGE ÄNDERUNG Wenn Sie kompilierende Code-Klammern für eine einzelne Zeile schreiben, hat dies keine Auswirkung. Wird Ihr Code jedoch interpretiert, verlangsamt dies den Code geringfügig. Kaum.

1
ST3

Immer geschweifte Klammern zu haben, ist eine sehr einfache und robuste Regel. Bei vielen geschweiften Klammern kann der Code jedoch unelegant aussehen. Wenn die Regeln es erlauben, geschweifte Klammern wegzulassen, sollten detailliertere Stilregeln und ausgefeiltere Werkzeuge vorhanden sein. Andernfalls kann es leicht zu chaotischem und verwirrendem (nicht elegantem) Code kommen. Daher ist es wahrscheinlich erfolglos, eine einzelne Stilregel von den übrigen verwendeten Stilrichtlinien und Werkzeugen zu trennen. Ich werde nur einige wichtige Details zu Regel 3 bringen, die in anderen Antworten noch nicht einmal erwähnt wurden.

Das erste interessante Detail ist, dass die meisten Befürworter dieser Regel zustimmen, sie im Falle von else zu verletzen. Mit anderen Worten, sie verlangen bei der Überprüfung keinen solchen Code:

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

Stattdessen können sie, wenn sie es sehen, sogar vorschlagen, es so zu schreiben:

// 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();
}

Dies verstößt technisch gegen diese Regel, da zwischen else und if keine geschweiften Klammern stehen. Eine solche Dualität der Regel tritt auf, wenn versucht wird, sie mit einem sinnlosen Werkzeug automatisch auf die Codebasis anzuwenden. In der Tat, warum zu streiten, lassen Sie einfach ein Werkzeug, um Stil automatisch anzuwenden.

Das zweite Detail (das auch von Befürwortern dieser Regel oft vergessen wird) ist, dass die Fehler, die auftreten können, niemals nur auf Verstöße gegen diese Regel Nr. 3 zurückzuführen sind. Tatsächlich handelt es sich dabei fast immer auch um Verstöße gegen Regel 1 (mit denen sich niemand auseinandersetzt). Auch unter dem Gesichtspunkt der automatischen Tools ist es nicht schwer, ein Tool zu erstellen, das sich sofort beschwert, wenn Regel 1 verletzt wird, sodass die meisten Fehler rechtzeitig abgefangen werden können.

Das dritte Detail (das von Gegnern dieser Regel oft vergessen wird) ist die verwirrende Natur der leeren Aussage, die durch ein einzelnes Semikolon dargestellt wird. Die meisten Entwickler mit etwas Erfahrung waren früher oder später verwirrt durch ein falsch platziertes Semikolon oder durch eine leere Anweisung, die mit einem Semikolon geschrieben wurde. Zwei geschweifte Klammern anstelle eines einzelnen Semikolons sind optisch viel leichter zu erkennen.

1
Öö Tiib

Es ist am besten, den Zeiger auf NULL zu setzen, wenn Sie damit fertig sind.

Hier ist ein Beispiel, warum:

Klasse A macht Folgendes:

  1. Ordnet einen Speicherblock zu
  2. Einige Zeit später löscht es diesen Speicherblock, setzt den Zeiger jedoch nicht auf NULL

Klasse B macht Folgendes

  1. Reserviert Speicher (und in diesem Fall erhält er zufällig denselben Speicherblock, der von Klasse A gelöscht wurde)

Zu diesem Zeitpunkt weisen sowohl Klasse A als auch Klasse B Zeiger auf denselben Speicherblock auf, was Klasse A betrifft, ist dieser Speicherblock nicht vorhanden, da er damit fertig ist.

Betrachten Sie das folgende Problem:

Was ist, wenn ein logischer Fehler in Klasse A dazu führte, dass in den Speicher geschrieben wurde, der jetzt zu Klasse B gehört?

In diesem speziellen Fall wird kein Ausnahmefehler mit ungültigem Zugriff angezeigt, da die Speicheradresse zulässig ist, während Klasse A die Klasse-B-Daten nun effektiv beschädigt.

Klasse B kann schließlich abstürzen, wenn es auf unerwartete Werte stößt, und wenn es abstürzt, werden Sie wahrscheinlich ziemlich viel Zeit damit verbringen, diesen Fehler in Klasse B zu suchen, wenn das Problem in Klasse A liegt.

Wenn Sie den Zeiger für den gelöschten Speicher auf NULL gesetzt hätten, hätten Sie einen Ausnahmefehler erhalten, sobald Logikfehler in Klasse A versucht hätten, in den NULL-Zeiger zu schreiben.

Wenn Sie wegen des Logikfehlers mit doppeltem Löschen besorgt sind, wenn die Zeiger zum zweiten Mal NULL sind, fügen Sie hierfür assert hinzu.

Auch: Wenn Sie abstimmen wollen, erklären Sie bitte.

1
John

Es gibt eine Reihe von Möglichkeiten, Steueranweisungen zu schreiben. Bestimmte Kombinationen können nebeneinander existieren, ohne die Lesbarkeit zu beeinträchtigen. Andere Kombinationen können jedoch zu Problemen führen. Der Style

if (condition)
  statement;

wird mit einigen der anderen Schreibweisen für Kontrollanweisungen problemlos zusammenleben, mit anderen jedoch nicht so gut. Wenn mehrzeilige kontrollierte Anweisungen wie folgt geschrieben werden:

if (condition)
{
  statement;
  statement;
}

dann ist es visuell offensichtlich, welche if -Anweisungen eine einzelne Zeile und welche mehrere Zeilen steuern. Wenn jedoch mehrzeilige if -Anweisungen wie folgt geschrieben werden:

if (condition) {
  statement;
  statement;
}

dann ist die Wahrscheinlichkeit, dass jemand versucht, if -Konstrukte mit einer einzigen Anweisung zu erweitern, ohne die erforderlichen geschweiften Klammern hinzuzufügen, viel höher.

Die Anweisung "single-statement-on-next line if" kann ebenfalls problematisch sein, wenn die Codebasis das Formular in erheblichem Maße nutzt

if (condition) statement;

Meine eigene Präferenz ist, dass die Anweisung in einer eigenen Zeile im Allgemeinen die Lesbarkeit verbessert, außer in Fällen, in denen es viele if -Anweisungen mit ähnlichen Steuerblöcken gibt, z.

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

in diesem Fall gehe ich solchen Gruppen von if -Anweisungen im Allgemeinen mit einer Leerzeile voran und folge ihnen, um sie visuell von anderem Code zu trennen. Wenn Sie eine Reihe von Anweisungen haben, die alle mit if am selben Einzug beginnen, erhalten Sie einen klaren visuellen Hinweis darauf, dass etwas Ungewöhnliches vorliegt.

1
supercat