340 Stimmen

Wozu dienen geschweifte Klammern (d.h. {}) in einer einzeiligen if- oder Schleife?

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

  1. Einrückung verwenden // OK
  2. Verlassen Sie sich nie auf den Vorrang von Operatoren - 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. Vorzeichenlose Variablen, die >= 0 sind, verwenden // netter Trick
  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 a { ... } ?

Nehmen wir zum Beispiel diesen merkwürdigen Code:

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

und ersetzen sie durch:

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

Was ist der Vorteil der 1. Version?

524voto

Luchian Grigore Punkte 244505

Lassen Sie uns versuchen, auch die i wenn wir inkrementieren j :

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

Oh nein! Von Python kommend, sieht das gut aus, ist es aber nicht, denn es ist äquivalent zu:

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

Das ist natürlich ein dummer Fehler, den aber auch ein erfahrener Programmierer machen kann.

Ein weiterer sehr guter Grund wird darauf hingewiesen in ta.speot.is's Antwort .

Eine dritte eine, die mir einfällt, ist verschachtelt if 's:

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

Nehmen wir nun an, Sie wollen doSomethingElse() wenn cond1 nicht erfüllt ist (neues Merkmal). Also:

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

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


Edit: Da dies einige Aufmerksamkeit erregt, werde ich meinen Standpunkt klarstellen. Die Frage, die ich beantworten wollte, lautet:

Was ist der Vorteil der 1. Version?

Das habe ich beschrieben. Es gibt einige Vorteile. Aber, IMO, "immer" Regeln gelten nicht immer. Daher unterstütze ich nicht vollständig

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

Ich sage nicht, dass 何時も verwenden eine {} blockieren. Wenn es eine einfache Bedingung und ein einfaches Verhalten ist, lassen Sie es. Wenn Sie vermuten, dass jemand später Ihren Code ändern könnte, um Funktionalität hinzuzufügen, tun Sie es.

332voto

ta.speot.is Punkte 26425

Es ist sehr einfach, versehentlich den Kontrollfluss mit Kommentaren zu ändern, wenn Sie nicht die { y } . Zum Beispiel:

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

must_always_do_this();

Wenn Sie auskommentieren do_something_else() mit einem einzeiligen Kommentar, erhalten Sie das folgende Ergebnis:

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

must_always_do_this();

Er kompiliert, aber must_always_do_this() wird nicht immer angerufen.

Wir hatten dieses Problem in unserer Codebasis, wo jemand einige Funktionen sehr schnell vor der Veröffentlichung deaktiviert hatte. Glücklicherweise haben wir es bei der Codeüberprüfung entdeckt.

60voto

James Kanze Punkte 146902

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

  1. OK
  2. Würde jemand wirklich schreiben (oder lesen wollen) (b*b) - ((4*a)*c) ? Einige Präzedenzfälle sind offensichtlich (oder sollten es sein), und die zusätzlichen Klammern tragen nur zur Verwirrung bei. (Andererseits _sollten_ Sie die Klammern Klammern in weniger offensichtlichen Fällen verwenden, auch wenn Sie wissen, dass sie nicht benötigt werden.)
  3. Mehr oder weniger. Es gibt zwei weit verbreitete Konventionen für die Formatierung Konditionale und Schleifen: if ( cond ) { code; } und: if ( cond ) { code; } Im ersten Fall würde ich ihm zustimmen. Die Eröffnung { ist nicht so sichtbar, also ist es am besten, davon auszugehen, dass sie immer da ist. Im zweiten Fall habe ich jedoch (und die meisten Leute, mit denen ich gearbeitet habe) kein Problem damit, die die geschweiften Klammern für eine einzelne Anweisung wegzulassen. (Vorausgesetzt natürlich, dass die Einrückung systematisch ist und Sie diesen Stil konsequent anwenden. (Und eine Menge sehr guter Programmierer, die sehr lesbaren Code schreiben, lassen die (Und viele sehr gute Programmierer, die sehr lesbaren Code schreiben, lassen die geschweiften Klammern sogar bei der ersten Formatierung weg).
  4. NO . Dinge wie if ( NULL == ptr ) hässlich genug sind, um die Lesbarkeit. Schreiben Sie die Vergleiche intuitiv. (Was in vielen Fällen zu der 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. NO . Alles andere als int ist für besondere Fälle reserviert. An erfahrenen C- und C++-Programmierern ist die Verwendung von unsigned Signale Bit Operatoren. C++ hat keinen echten Kardinaltyp (oder einen anderen effektiven Unterbereichstyp); unsigned funktioniert nicht für numerische Werte, wegen der Beförderungsregeln. Numerische Werte, bei denen keine arithmetische Operationen sinnvoll wären, wie z. B. Seriennummern, könnten vermutlich sein unsigned . Ich würde jedoch dagegen argumentieren, weil es die falsche Botschaft sendet: Bitweise Operationen sind auch nicht sinnvoll. Die Grundregel ist, dass integrale Typen sind int es sei denn, es gibt eine einen wichtigen Grund für die Verwendung eines anderen Typs.
  6. NO . Dies systematisch zu tun, ist irreführend und schützt nicht wirklich schützt vor nichts. In strengem OO-Code, delete this; ist oft der häufigste Fall (und man kann nicht this a NULL ), und Ansonsten sind die meisten delete befinden sich in Destruktoren, daher kann man nicht auf die Zeiger später sowieso nicht zugreifen. Und wenn man ihn auf NULL tut nichts mit anderen Zeigern, die im Umlauf sind. Das Setzen des Zeigers systematisch auf NULL ein falsches Gefühl der Sicherheit vermittelt und nicht wirklich nichts.

Schauen Sie sich den Code in einer der typischen Referenzen an. Stroustrup verstößt gegen jede Regel, die Sie angegeben haben, außer der ersten, zum Beispiel.

Ich würde vorschlagen, dass Sie sich einen anderen Dozenten suchen. Einen, der wirklich weiß, wovon wovon er spricht.

49voto

Konrad Rudolph Punkte 503837

Alle anderen Antworten verteidigen die Regel 3 Ihres Dozenten.

Ich möchte sagen, dass ich mit Ihnen übereinstimme: die Regel ist überflüssig und ich würde nicht dazu raten. Es ist wahr, dass es theoretisch verhindert Fehler, wenn Sie immer geschweifte Klammern hinzufügen. Andererseits, Dieses Problem ist mir im wirklichen Leben noch nie begegnet. Im Gegensatz zu dem, was andere Antworten vermuten lassen, habe ich nicht ein einziges Mal vergessen, die geschweiften Klammern hinzuzufügen, sobald sie notwendig wurden. Wenn Sie eine korrekte Einrückung verwenden, wird es sofort klar, dass Sie geschweifte Klammern hinzufügen müssen, sobald mehr als eine Anweisung eingerückt ist.

Die Antwort von Komponente 10 weist auf den einzigen denkbaren Fall hin, in dem dies wirklich zu einem Fehler führen könnte. Andererseits ist bei der Ersetzung von Code durch reguläre Ausdrücke ohnehin immer große Vorsicht geboten.

Betrachten wir nun die andere Seite der Medaille: Gibt es eine Nachteil immer geschweifte Klammern zu verwenden? Die anderen Antworten ignorieren diesen Punkt einfach. Aber es es ein Nachteil: Er nimmt viel vertikalen Platz auf dem Bildschirm ein, was wiederum dazu führen kann, dass Ihr Code unlesbar wird, weil 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, dass der folgende Code wesentlich 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);
}

Auch bei kurzen, verschachtelten Schleifen ist es von Vorteil, die geschweiften Klammern wegzulassen:

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

Vergleiche 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 gemildert werden bis zu einem gewissen Grad indem Sie die öffnende geschweifte Klammer auf die vorherige Zeile setzen. Wenn Sie also auf geschweifte Klammern bestehen, setzen Sie zumindest die öffnende Klammer auf die vorherige Zeile.

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


In der Zeit, seit ich die Antwort geschrieben habe, habe ich den vorherrschenden Codestil größtenteils akzeptiert und verwende geschweifte Klammern, es sei denn, ich kann die gesamte einzelne Anweisung in die vorherige Zeile setzen. Ich bin immer noch der Meinung, dass der Verzicht auf redundante geschweifte Klammern in der Regel besser lesbar ist, und ich habe noch nie auf einen dadurch verursachten Fehler gestoßen.

41voto

jam Punkte 3610

Die Codebasis, an der ich arbeite, ist übersät mit Code von Leuten, die eine pathologische Abneigung gegen geschweifte Klammern haben, und für die Leute, die später dazukommen, kann es wirklich einen Unterschied in der Wartbarkeit machen.

Das häufigste problematische Beispiel, das mir begegnet ist, ist dieses:

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 eine "Dann"-Anweisung hinzufügen möchte, kann es leicht zu diesem Ergebnis kommen, wenn ich nicht vorsichtig bin:

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

Wenn man bedenkt, dass das Hinzufügen von geschweiften Klammern ~1 Sekunde dauert und mindestens ein paar verwirrte Minuten bei der Fehlersuche einsparen kann, warum sollten Sie jemals no die Option der reduzierten Zweideutigkeit wählen? Das scheint mir eine falsche Einsparung zu sein.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X