Qual è lo scopo dell'uso delle parentesi graffe (ad es. {}) Per un ciclo if o a riga singola?


321

Sto leggendo alcuni appunti del mio docente di C ++ e ha scritto quanto segue:

  1. Usa rientro // OK
  2. Non fare mai affidamento sulla precedenza dell'operatore - Usa sempre le parentesi // OK
  3. Usa sempre un blocco {} - anche per una singola riga // non OK , perché ???
  4. Const oggetto sul lato sinistro del confronto // OK
  5. Usa unsigned per le variabili che sono> = 0 // bel trucco
  6. Impostare il puntatore su NULL dopo l'eliminazione - Doppia protezione dall'eliminazione // non male

La terza tecnica non mi è chiara: cosa guadagnerei posizionando una riga in a { ... }?

Ad esempio, prendi questo strano codice:

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

e sostituiscilo con:

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

Quali sono i vantaggi dell'utilizzo della prima versione?


256
Leggibilità e manutenibilità. Non è immediatamente ovvio a quale blocco di istruzioni 'j ++' appartenga e che l'aggiunta di codice dopo che non sia associata all'istruzione if.
La foresta e gli alberi

27
Mi è sempre stato detto di usare le parentesi graffe {} per queste righe per un paio di motivi. Rende il codice più chiaro da leggere. Anche qualcun altro tra sei mesi potrebbe aver bisogno di modificare il codice, quindi la chiarezza è importante e con le parentesi graffe è meno probabile che si verifichi un errore. Non c'è nulla di tecnicamente più corretto al riguardo, è più solo una questione di buone pratiche. Tieni presente che un progetto può avere migliaia e migliaia di righe di codice da far sfogliare per un nuovo ragazzo!
RossC,

30
Non sono d'accordo con 6, in quanto nasconderà una doppia eliminazione e potenzialmente nasconderà errori logici.
Luchian Grigore,

28
# 5 potrebbe essere ingannevole - considerare questo ciclo: for (unsigned i = 100; i >= 0; --i).
Archie,

36
A proposito, (i % 2 == 0)contraddice (2). Stai facendo affidamento sulla precedenza dell'operatore e il significato è ovviamente ((i % 2) == 0)piuttosto che (i % (2 == 0)). Classificherei la regola 2 come "un sentimento valido ma" sempre "è sbagliato".
Steve Jessop,

Risposte:


509

Proviamo a modificare anche iquando incrementiamo j:

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

Oh no! Proveniente da Python, questo sembra ok, ma in realtà non lo è, in quanto equivale a:

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

Certo, questo è un errore sciocco, ma che anche un programmatore esperto potrebbe fare.

Un altro ottimo motivo è indicato nella risposta di ta.speot.is .

Un terzo che mi viene in mente è nidificato if:

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

Ora, supponiamo che ora lo desideri doSomethingElse()quando cond1non è soddisfatto (nuova funzionalità). Così:

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

che è ovviamente sbagliato, dal momento che i elsesoci con l'interno if.


Modifica: dal momento che sta ottenendo una certa attenzione, chiarirò il mio punto di vista. La domanda a cui stavo rispondendo è:

Quali sono i vantaggi dell'utilizzo della prima versione?

Che ho descritto. Ci sono alcuni vantaggi Ma, IMO, le regole "sempre" non si applicano sempre. Quindi non sostengo del tutto

Usa sempre un blocco {} - anche per una singola riga // non OK, perché ???

Non sto dicendo di usare sempre un {}blocco. Se è una condizione e un comportamento abbastanza semplici, non farlo. Se sospetti che qualcuno possa entrare in seguito e modificare il codice per aggiungere funzionalità, fallo.


5
@Science_Fiction: True, ma se aggiungi i++ prima j++ , entrambe le variabili saranno ancora nell'ambito quando vengono utilizzate.
Mike Seymour,

26
Sembra molto ragionevole, ma trascura il fatto che l'editor fa il rientro, non tu, e lo indenterà i++;in un modo che mostra immediatamente che non fa parte del ciclo. (In passato, questo potrebbe essere stato un argomento ragionevole, e ho visto tali problemi. Circa 20 anni fa. Non da allora.)
James Kanze,

43
@James: non è un "fatto", tuttavia, è il tuo flusso di lavoro. E il flusso di lavoro di molte persone, ma non di tutti. Non penso che sia necessariamente un errore trattare la fonte C ++ come un semplice file di testo, piuttosto che l'output di un editor WYSIWYG (vi / emacs / Visual Studio) che impone regole di formattazione. Quindi questa regola è indipendente dall'editore oltre ciò di cui hai bisogno, ma non oltre ciò che le persone usano effettivamente per modificare il C ++. Da qui "difensivo".
Steve Jessop,

31
@JamesKanze Stai davvero facendo affidamento sul presupposto che tutti lavorano sempre con potenti IDE? L'ultimo CI scritto è stato a Nano. Ciò premesso, una delle prime cose che tendo a disattivare in un IDE è il rientro automatico, poiché l'IDE tende a ostacolare il mio flusso di lavoro non lineare , cercando di correggere i miei "errori" sulla base di informazioni incomplete . Gli IDE non sono molto bravi a rientrare automaticamente nel flusso naturale di ogni programmatore. Quei programmatori che usano tali funzionalità tendono a fondere il loro stile con il loro IDE, il che va bene se usi un solo IDE ma non molto se lavori in molti.
Rushyo,

10
"Questo è un errore sciocco, ma che anche un programmatore esperto potrebbe fare." - Come ho detto nella mia risposta, non ci credo. Penso che sia un caso interamente inventato che non rappresenta un problema nella realtà.
Konrad Rudolph,

324

È molto facile cambiare accidentalmente il flusso di controllo con commenti se non lo si utilizza {e }. Per esempio:

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

must_always_do_this();

Se commenti do_something_else()con un commento a riga singola, finirai con questo:

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

must_always_do_this();

Si compila, ma must_always_do_this()non viene sempre chiamato.

Abbiamo riscontrato questo problema nella nostra base di codice, in cui qualcuno aveva disattivato alcune funzionalità molto rapidamente prima del rilascio. Fortunatamente l'abbiamo preso nella revisione del codice.


3
Ohh ragazzo !! è un comportamento definito che must_always_do_this();verrà eseguito se si commenta // do_something_else ();
Mr. Anubis,

1
@Supr, come è stato scritto per la prima volta, sta dicendo che è difficile interrompere il flusso corretto se si utilizzano le parentesi graffe, e quindi fornisce un esempio di quanto sia facile interrompere senza avere il codice correttamente tra parentesi
SeanC

20
Mi sono imbattuto in questo solo l'altro giorno. if(debug) \n //print(info);. Praticamente tirò fuori un'intera biblioteca.
Kevin,

4
Fortunately we caught it in code review.Ahia! Sembra così sbagliato. Fortunately we caught it in unit tests.sarebbe molto meglio!
BЈовић,

4
@ BЈовић Ma cosa succede se il codice era in un test unitario? La mente vacilla. (Sto scherzando, è un'app legacy. Non ci sono test unitari.)
ta.speot.is

59

Ho i miei dubbi sulla competenza del docente. Considerando i suoi punti:

  1. ok
  2. Qualcuno scriverà davvero (o vorrebbe leggere) (b*b) - ((4*a)*c)? Alcune precedenti sono ovvie (o dovrebbero esserlo) e le parentesi extra aggiungono solo confusione. (D'altra parte, dovresti usare le parentesi in casi meno ovvi, anche se sai che non sono necessari.)
  3. Una specie di. Esistono due convenzioni diffuse per la formattazione di condizionali e loop:
    if (cond) {
        codice;
    }
    
    e:
    if (cond)
    {
        codice;
    }
    
    Nel primo, sono d'accordo con lui. L'apertura {non è così visibile, quindi è meglio supporre che sia sempre lì. Nel secondo, tuttavia, io (e la maggior parte delle persone con cui ho lavorato) non ho alcun problema a omettere le parentesi graffe per una singola affermazione. (A condizione, ovviamente, che il rientro sia sistematico e che usi questo stile in modo coerente. (E molti programmatori molto bravi, scrivendo codice molto leggibile, omettono le parentesi graffe anche quando si formatta il primo modo.)
  4. NO . Cose del genere if ( NULL == ptr )sono abbastanza brutte da ostacolare la leggibilità. Scrivi i confronti in modo intuitivo. (Il che in molti casi si traduce nella costante a destra.) Il suo 4 è un cattivo consiglio; tutto ciò che rende il codice innaturale lo rende meno leggibile.
  5. NO . Tutto tranne che intè riservato per casi speciali. Per programmatori esperti in C e C ++, l'uso di unsignedsegnali bit operatori. Il C ++ non ha un vero tipo cardinale (o qualsiasi altro tipo di subrange efficace); unsignednon funziona per valori numerici, a causa delle regole di promozione. Valori numerici sui quali nessuna operazione aritmetica avrebbe senso, come i numeri di serie, presumibilmente potrebbero esserlo unsigned. Discuterei contro, tuttavia, perché invia il messaggio sbagliato: neanche le operazioni bit a bit hanno senso. La regola di base è che i tipi integrali sono int_unless_ c'è una ragione significativa per usare un altro tipo.
  6. NO . Farlo sistematicamente è fuorviante e in realtà non protegge da nulla. Nel rigoroso codice OO, delete this;è spesso il caso più frequente (e non è possibile impostarlo thissu NULL), altrimenti la maggior parte deletesono in distruttori, quindi non è possibile accedere al puntatore in seguito comunque. E impostarlo su NULLnon fa nulla riguardo ad altri puntatori che fluttuano intorno. Impostando il puntatore in modo sistematico su si NULLottiene un falso senso di sicurezza e non si acquista davvero nulla.

Guarda il codice in uno dei riferimenti tipici. Stroustrup viola ogni regola che hai dato ad eccezione della prima, ad esempio.

Ti suggerirei di trovare un altro docente. Uno che sa davvero di cosa sta parlando.


13
Il numero 4 potrebbe essere brutto ma c'è uno scopo. Sta cercando di impedire if (ptr = NULL). Non credo di aver mai usato delete this, è più comune di quanto abbia visto? Non tendo a pensare che impostare un puntatore su NULL dopo l'uso sia una cosa brutta da fare se non YMMV. Forse sono solo io, ma la maggior parte delle sue linee guida non sembrano così male.
Firedragon,

16
@Firedragon: la maggior parte dei compilatori ti avviserà if (ptr = NULL)se non lo scrivi come if ((ptr = NULL)). Devo essere d'accordo con James Kanze sul fatto che la bruttezza di averlo NULLprima lo rende un NO definitivo per me.
Leone

34
@JamesKanze: Devo dire che non sono d'accordo con la maggior parte di ciò che hai affermato qui, anche se apprezzo e rispetto i tuoi argomenti per essere arrivati ​​a loro. Per i programmatori esperti C e C ++, l'uso di operatori bit di segnali senza segno. - Non sono affatto d'accordo: l'uso degli operatori bit segnala l'uso di operatori bit. Per me l'uso di unsignedindica un'aspirazione da parte del programmatore che la variabile dovrebbe rappresentare solo numeri positivi. La miscelazione con numeri firmati di solito provoca un avvertimento del compilatore che era probabilmente quello che il docente intendeva.
Componente 10

18
Per i programmatori esperti C e C ++, l'uso di operatori bit di segnali non firmati oppure no. size_tqualcuno?
ta.speot.is

16
@James Kanze, considera lo scopo. Stai confrontando il codice prodotto da un programmatore esperto con esempi didattici. Queste regole sono fornite dal docente perché sono i tipi di errori che vede fare i suoi studenti. Con l'esperienza, gli studenti possono rilassarsi o ignorare questi assoluti.
Joshua Shane Liberman,

46

Tutte le altre risposte difendono la regola del docente 3.

Lasciami dire che sono d'accordo con te: la regola è ridondante e non la consiglierei. È vero che teoricamente previene gli errori se aggiungi sempre parentesi graffe. D'altra parte, non ho mai riscontrato questo problema nella vita reale : contrariamente a quanto implicano altre risposte, non ho dimenticato una volta di aggiungere le parentesi graffe una volta diventate necessarie. Se si utilizza il rientro corretto, diventa immediatamente ovvio che è necessario aggiungere parentesi graffe una volta rientrata più di un'istruzione.

La risposta di "Component 10" evidenzia in realtà l'unico caso immaginabile in cui ciò potrebbe davvero portare a un errore. D'altra parte, la sostituzione del codice con l'espressione regolare richiede comunque un'enorme cura.

Ora diamo un'occhiata all'altro lato della medaglia: c'è uno svantaggio di usare sempre parentesi graffe? Le altre risposte ignorano semplicemente questo punto. Ma v'è uno svantaggio: ci vuole un sacco di spazio sullo schermo in verticale, e questo a sua volta può rendere il codice illeggibile, perché significa che si deve scorrere più del necessario.

Considera una funzione con molte clausole guard all'inizio (e sì, il seguente è un codice C ++ errato ma in altre lingue questa sarebbe una situazione abbastanza comune):

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

Questo è un codice orribile, e sostengo fortemente che quanto segue sia molto più leggibile:

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

Allo stesso modo, i cicli nidificati brevi traggono beneficio dall'omissione delle parentesi graffe:

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

Confrontare con:

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

Il primo codice è conciso; il secondo codice è gonfio.

E sì, questo può essere mitigato in una certa misura mettendo la parentesi graffa di apertura sulla riga precedente. Ma sarebbe comunque meno leggibile del codice senza parentesi graffe.

In breve: non scrivere codice non necessario che occupa spazio sullo schermo.


26
Se non credi nella scrittura di codice che occupa inutilmente spazio sullo schermo, allora non hai affari a mettere la parentesi graffa sulla sua stessa linea. Probabilmente ora dovrò schivare e scappare dalla sacra vendetta di GNU, ma seriamente - o vuoi che il tuo codice sia compatto verticalmente, oppure no. E se lo fai, non fare cose progettate esclusivamente per rendere il tuo codice meno verticalmente compatto. Ma come dici tu, dopo aver determinato che, saresti ancora anche da rimuovere parentesi ridondanti. O forse basta scrivere if (a == nullptr) { throw null_ptr_error("a"); }come una riga.
Steve Jessop,

6
@Steve È un dato di fatto, ho faccio mettere la parentesi di apertura sulla linea precedente, per la ragione lei ha affermato. Ho usato l'altro stile qui per rendere più ovvio quanto estrema possa essere la differenza.
Konrad Rudolph,

9
+1 Sono completamente d'accordo sul fatto che il tuo primo esempio è molto più facile da leggere senza parentesi graffe. Nel secondo esempio, il mio stile di codifica personale consiste nell'utilizzare le parentesi graffe sul for-loop esterno e non sull'interno. Non sono d'accordo con @SteveJessop sul fatto di dover essere un estremo o l'altro sul codice verticalmente compatto. Ometto le parentesi graffe aggiuntive con una sola linea per ridurre lo spazio verticale, ma inserisco le parentesi graffe di apertura su una nuova linea perché trovo più facile vedere l'ambito quando le parentesi sono allineate. L'obiettivo è la leggibilità e talvolta ciò significa utilizzare più spazio verticale, altre volte significa utilizzare meno.
Travesty3,

22
"Non ho mai riscontrato questo problema nella vita reale": beato te. Cose come questa non solo ti bruciano, ti danno il 90% di ustioni di terzo grado (e questo è solo un paio di livelli di gestione che richiedono una correzione a tarda sera).
Richard,

9
@Richard Semplicemente non lo compro. Come ho spiegato nella chat, anche se questo errore dovesse mai verificarsi (che trovo improbabile), è banale riparare una volta che si guarda la traccia dello stack perché è ovvio dove si trova l'errore semplicemente guardando il codice. La tua richiesta esagerata è completamente priva di fondamento.
Konrad Rudolph,

40

La base di codice su cui sto lavorando è disseminata di codice da parte di persone con avversione patologica alle parentesi graffe e, per le persone che vengono dopo, può davvero fare la differenza per la manutenibilità.

L'esempio problematico più frequente che ho riscontrato è questo:

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;

Quindi quando arrivo e desidero aggiungere un'istruzione then, posso facilmente finire con questo se non sto attento:

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

Dato che ci vuole ~ 1 secondo per aggiungere le parentesi graffe e si può risparmiare al minimo pochi minuti confusi debug, perché mai non andare con l'opzione ridotto l'ambiguità? Mi sembra una falsa economia.


16
Il problema in questo esempio non è il rientro improprio e le righe troppo lunghe anziché le parentesi graffe?
Honza Brabec,

7
Sì, ma seguire le linee guida di progettazione / codifica che sono solo "sicure" supponendo che le persone seguano anche altre linee guida (come non avere linee troppo lunghe) sembra chiedere problemi. Se le parentesi graffe fossero state dall'inizio, sarebbe impossibile finire con un if-block errato in questa situazione.
marmellata

16
Come aggiungere le parentesi graffe ( if(really long...editor){ do_foo;}aiutandoti a evitare questo caso? Sembra che il problema sarebbe sempre lo stesso. Personalmente preferisco evitare le parentesi graffe quando non sono necessarie, tuttavia ciò non ha nulla a che fare con il tempo necessario per scriverle ma la leggibilità ridotta a causa delle due righe extra nel codice.
Grizzly

1
Un buon punto: supponevo che l'applicazione forzata delle parentesi graffe avrebbe comportato anche la loro collocazione in un posto ragionevole, ma ovviamente qualcuno determinato a rendere le cose difficili potrebbe metterle in linea come nel tuo esempio. Immagino che la maggior parte delle persone non lo farebbe, comunque.
marmellata

2
La prima e l'ultima cosa che faccio quando si tocca un file è premere il pulsante di formattazione automatica. Elimina la maggior parte di questi problemi.
Jonathan Allen,

20

Il mio 2c:

Usa rientro

Ovviamente

Non fare mai affidamento sulla precedenza dell'operatore - Utilizzare sempre le parentesi

Non userei le parole "mai e" sempre ", ma in generale vedo questa regola utile. In alcune lingue (Lisp, Smalltalk) questo non è un problema.

Usa sempre un blocco {}, anche per una singola riga

Non lo faccio mai e non ho mai avuto un singolo problema, ma posso vedere come può essere buono per gli studenti, esp. se avessero studiato Python prima.

Costante oggetto sul lato sinistro del confronto

Condizioni di Yoda? No per favore. Fa male la leggibilità. Usa il massimo livello di avviso quando compili il tuo codice.

Usa unsigned per variabili che sono> = 0

OK. Abbastanza divertente, ho sentito che Stroustrup non è d'accordo.

Impostare il puntatore su NULL dopo l'eliminazione: doppia protezione dall'eliminazione

Pessimo consiglio! Non avere mai un puntatore che punta a un oggetto cancellato o inesistente.


5
+1 solo per l'ultimo punto da solo. Un puntatore non elaborato non ha comunque alcuna proprietà aziendale.
Konrad Rudolph,

2
Per quanto riguarda l'utilizzo di unsigned: non solo Stroustrup, ma K&R (in C), Herb Sutter e (penso) Scott Meyers. In realtà, non ho mai sentito nessuno che capisse veramente le regole del C ++ discutere di usare unsigned.
James Kanze,

2
@JamesKanze In effetti, nella stessa occasione ho sentito l'opinione di Stroustrup (una conferenza di Boston nel 2008), Herb Sutter era lì e non era d'accordo con Bjarne sul posto.
Nemanja Trifunovic,

3
Solo per completare " unsignedè rotto", uno dei problemi è che quando C ++ confronta tipi firmati e non firmati di dimensioni simili, si converte in non firmati prima di fare il confronto. Ciò si traduce in una variazione di valore. Passare alla firma non sarebbe necessariamente molto meglio; il confronto dovrebbe realmente avvenire "come se" entrambi i valori fossero convertiti in un tipo più grande che potesse rappresentare tutti i valori in entrambi i tipi.
James Kanze,

1
@SteveJessop Penso che devi prenderlo nel contesto di una funzione di ritorno unsigned. Sono sicuro che non ha problemi a exp(double)restituire un valore superiore a MAX_INT:-). Ma ancora una volta, il vero problema sono le conversioni implicite. int i = exp( 1e6 );è C ++ perfettamente valido. Stroustrup in realtà propose di deprecare le conversioni implicite con perdita di dati a un certo punto, ma il comitato non era interessato. (Una domanda interessante: sarebbe unsigned-> intessere considerato in perdita. Considererei entrambi unsigned-> inte int-> in unsignedperdita. Il che farebbe molto per rendere unsignedOK
James Kanze,

18

è più intuitivo e facilmente comprensibile. Rende chiaro l'intento.

E assicura che il codice non si rompa quando un nuovo utente potrebbe inconsapevolmente perdere il {, }mentre aggiunge una nuova istruzione di codice.


Makes the intent clear+1, questa è probabilmente la ragione più concisa e accurata.
Qix - MONICA È STATA MISTREATA il

13

Per aggiungere ai suggerimenti molto sensati sopra, un esempio che ho riscontrato durante il refactoring di un codice in cui questo diventa critico era il seguente: Stavo modificando una base di codice molto grande per passare da un'API all'altra. La prima API ha ricevuto una chiamata per impostare l'ID azienda come segue:

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

mentre la sostituzione ha richiesto due chiamate:

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

Ho deciso di cambiarlo usando espressioni regolari che hanno avuto molto successo. Abbiamo anche passato il codice attraverso astyle , il che lo ha reso molto più leggibile. Quindi, a metà del processo di revisione, ho scoperto che in alcune circostanze condizionate stava cambiando questo:

if ( condition )
   setCompIds( compId, compSubId );

A questa:

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

che chiaramente non è ciò che era richiesto. Ho dovuto tornare all'inizio per farlo di nuovo trattando la sostituzione come completamente all'interno di un blocco e quindi alterando manualmente tutto ciò che alla fine sembrava sciocco (almeno non sarebbe errato.)

Ho notato che astyle ora ha l'opzione --add-bracketsche ti consente di aggiungere parentesi dove non ce ne sono e lo consiglio vivamente se ti trovi mai nella stessa posizione in cui ero.


5
Una volta vidi della documentazione che conteneva il meraviglioso conio "Microsoftligent". Sì, è possibile commettere errori significativi con la ricerca globale e la sostituzione. Ciò significa solo che la ricerca e la sostituzione globali devono essere utilizzate in modo intelligente, non microsoftligently.
Pete Becker,

4
So che questo non è il mio post-mortem da eseguire, ma se hai intenzione di fare la sostituzione del testo sul codice sorgente, dovresti farlo secondo le stesse regole che useresti per il tipo di sostituzione del testo che è ben stabilito nella lingua: macro. Non dovresti scrivere una macro #define FOO() func1(); \ func2(); (con una interruzione di riga dopo la barra rovesciata), lo stesso vale per la ricerca e la sostituzione. Detto questo, ho visto "usare sempre le parentesi graffe" avanzate come regola di stile proprio perché ti evita di racchiudere tutte le tue macro multiistruzione do .. while(0). Ma non sono d'accordo.
Steve Jessop,

2
A proposito, questo è "ben consolidato", nel senso che il knotweed giapponese è ben consolidato: non sto dicendo che dovremmo fare di tutto per usare le macro e la sostituzione del testo, ma sto dicendo che quando facciamo un tale cosa, dovremmo farlo in un modo che funzioni, piuttosto che fare qualcosa che funzioni solo se una particolare regola di stile è stata imposta con successo su tutta la base di codice :-)
Steve Jessop

1
@SteveJessop Uno potrebbe anche discutere per parentesi graffe e una cintura. Se devi usare tali macro (e l'abbiamo fatto, prima di C ++ e inline), allora dovresti probabilmente mirare a farle funzionare il più possibile come una funzione, usando il do { ... } while(0)trucco se necessario (e molte parentesi extra. non ti impedirà di usare le parentesi graffe ovunque, se quello è lo stile della casa. (FWIW: Ho lavorato in luoghi con diversi stili di casa, coprendo tutti gli stili discussi qui. Non ho mai trovato alcun problema serio.)
James Kanze,

1
E credo, più stili hai lavorato con più leggi e modifichi attentamente il codice. Quindi, anche se hai una preferenza su ciò che è più facile da leggere, continuerai a leggere con successo gli altri. Ho lavorato in un'azienda in cui diversi componenti sono stati scritti in diversi "stili di casa" dai diversi team e la soluzione corretta è quella di lamentarsi nella sala da pranzo senza alcun risultato, non di cercare di creare uno stile globale :-)
Steve Jessop,

8

Sto usando {}ovunque tranne alcuni casi in cui è ovvio. La linea singola è uno dei casi:

if(condition) return; // OK

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

Potrebbe farti male quando aggiungi qualche metodo prima del ritorno. Il rientro indica che il ritorno è in esecuzione quando viene soddisfatta la condizione, ma tornerà sempre.

Altro esempio in C # con l'utilizzo di statment

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

che equivale a

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

1
Basta usare le virgole using
nell'istruzione

1
@minitech Qui semplicemente non funziona: puoi usare la virgola solo quando i tipi sono uguali, non per tipi diversi. Il modo di Lukas di farlo è il modo canonico, l'IDE lo formatta anche in modo diverso (notare la mancanza di rientro automatico del secondo using).
Konrad Rudolph,

8

L'esempio più pertinente che mi viene in mente:

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

Con quale ifsarà elseaccoppiato? Il rientro implica che l'esterno ifottenga il else, ma non è così che il compilatore lo vedrà; l' interno if otterrà elsee l'esterno ifno. Dovresti sapere che (o vederlo comportarsi in quel modo in modalità di debug) per capire perché questo codice potrebbe non soddisfare le tue aspettative. Diventa più confuso se conosci Python; in quel caso sai che il rientro definisce i blocchi di codice, quindi ti aspetteresti che valuti in base al rientro. C #, tuttavia, non dà una svolta al volo per gli spazi bianchi.

Ora, detto questo, non sono particolarmente d'accordo con questa regola "usa sempre le parentesi" sulla sua faccia. Rende il codice molto verticalmente rumoroso, riducendo la capacità di leggerlo rapidamente. Se la dichiarazione è:

if(someCondition)
   DoSomething();

... allora dovrebbe essere scritto proprio così. L'istruzione "usa sempre parentesi" suona come "circonda sempre le operazioni matematiche tra parentesi". Ciò trasformerebbe la semplice affermazione a * b + c / din ((a * b) + (c / d)), introducendo la possibilità di perdere un paren vicino (la rovina di molti programmatori), e per cosa? L'ordine delle operazioni è ben noto e applicato, quindi le parentesi sono ridondanti. Utilizzeresti solo le parentesi per imporre un ordine delle operazioni diverso da quello normalmente applicato: a * (b+c) / dad esempio. Le parentesi graffe sono simili; usali per definire cosa vuoi fare nei casi in cui differisce dal valore predefinito e non è "ovvio" (soggettivo, ma di solito piuttosto buon senso).


3
@AlexBrown ... che era esattamente il mio punto. La regola come indicato nel PO è "usa sempre parentesi, anche per linee singole", che non sono d'accordo con il motivo che ho affermato. Le parentesi potrebbero essere di aiuto con il primo esempio di codice, poiché il codice non si comporterà nel modo in cui è rientrato; dovresti usare le parentesi per accoppiare elseil primo ifinvece del secondo. Rimuovere il downvote.
KeithS

5

Guardando attraverso le risposte nessuno ha dichiarato esplicitamente il tipo di pratica di cui ho l'abitudine, raccontando la storia del tuo codice:

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

diventa:

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

Mettere il j++sulla stessa linea come se dovrebbe segnalare a chiunque altro, "Voglio solo che questo blocco di incremento mai j" . Di coursethis vale la pena solo se la linea è semplice possibile, perché mettere un punto di interruzione qui, come peri menzioni, non sta per essere molto utile.

In effetti ho appena incontrato parte dell'API Storm di Twitter che ha questo "tipo" di codice in Java, ecco lo snippet relvant dal codice di esecuzione, a pagina 43 di questa presentazione :

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

Il blocco del ciclo for ha due elementi, quindi non vorrei incorporare quel codice. Cioè mai :

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

È orribile e non so nemmeno se funziona (come previsto); non farlo . Nuove linee e parentesi graffe aiutano a distinguere parti di codice separate ma correlate, come fanno in prosa una virgola o un punto e virgola. Il blocco sopra è una frase davvero lunga con alcune clausole e alcune altre dichiarazioni che non si rompono o si fermano mai per distinguere parti separate.

Se vuoi davvero telegrafare a qualcun altro è un lavoro su una sola riga, usa un operatore ternario o un ?:modulo:

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

Ma questo sta rasentando il code-golf, e penso che non sia una buona pratica (non mi è chiaro se dovrei mettere j ++ da un lato :o meno). NB: non ho mai eseguito un operatore ternario in C ++ prima, non so se funziona, ma esiste .

In breve:

Immagina come il tuo lettore (cioè la persona che mantiene il codice) interpreta la tua storia (codice). Renderlo il più chiaro possibile per loro. Se sai che lo studente / programmatore principiante lo sta mantenendo, forse lascialo nel maggior numero {}possibile, solo per non confonderlo.


1
(1) Mettere la frase sulla stessa riga la rende meno leggibile, non di più. Pensieri particolarmente semplici come un incremento sono facilmente trascurati. Mettili su una nuova linea. (2) Ovviamente puoi mettere il tuo forloop su una sola riga, perché non dovrebbe funzionare? Funziona per lo stesso motivo per cui è possibile omettere le parentesi graffe; newline non è semplicemente significativo in C ++. (3) Il tuo esempio di operatore condizionale, oltre ad essere orribile, non è C ++ valido.
Konrad Rudolph,

@KonradRudolph grazie, sono un po 'arrugginito al C ++. Non ho mai detto (1) che fosse più leggibile, ma segnalerebbe che quel pezzo di codice doveva essere online una riga. (2) Il mio commento è stato più che non sarei in grado di leggerlo e sapere che ha funzionato, sia che sia o come previsto; è un esempio di cosa non fare per questo motivo. (3) Grazie, non scrivo C ++ da molto tempo. Lo riparerò ora.
Pureferret,

2
Mettere anche più di un'espressione in una riga rende più difficile il debug del codice. Come si mette il punto di interruzione nella seconda espressione in quella riga?
Piotr Perak,

5

Perché quando hai due dichiarazioni senza {}, è facile perdere un problema. Supponiamo che il codice assomigli a questo.

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;

Sembra a posto. Il problema con esso è davvero facile da perdere, specialmente quando la funzione che contiene il codice è molto più grande. Il problema è che goto failviene eseguito incondizionatamente. Puoi facilmente immaginare quanto sia frustrante (farti chiedere perché l'ultimo hash_updatefallisce sempre, dopo tutto sembra hash_updatefunzionare bene ).

Tuttavia, ciò non significa che sto per aggiungere {}ovunque (a mio avviso, vedere {}ovunque è fastidioso). Anche se può causare problemi, non lo è mai stato per i miei progetti, poiché il mio stile di codifica personale proibisce i condizionali senza {}quando non sono sulla stessa linea (sì, sono d'accordo che il mio stile di codifica non è convenzionale, ma mi piace, e io utilizzare lo stile di codice del progetto quando si contribuisce ad altri progetti). Questo rende bene il seguente codice.

if (something) goto fail;

Ma non quello seguente.

if (something)
    goto fail;

Esattamente. Basta non inserire il trattino (completamente inutile) newline + e eludere completamente questo problema che tutti sono sempre così veloci da sollevare.
Alexander - Ripristina Monica

4

Rende il tuo codice più leggibile definendo chiaramente l'ambito dei tuoi loop e blocchi condizionali. Ti salva anche da errori accidentali.


4

wrt 6: è più sicuro perché eliminare un puntatore null è un no-op. Quindi, se ti capita di percorrere accidentalmente quel percorso due volte, non causerai il danneggiamento della memoria liberando la memoria che è libera o è stata allocata a qualcos'altro.

Questo è in gran parte un problema con oggetti di ambito di file statici e singoli che non hanno una vita molto chiara e sono stati conosciuti per essere ricreati dopo che sono stati distrutti.

Nella maggior parte dei casi, puoi evitare la necessità usando auto_ptrs


6
Se ti capita di attraversare quel percorso due volte, hai un errore di programmazione. L'impostazione di un puntatore su null per rendere questo errore meno dannoso non risolve il problema sottostante.
Pete Becker,

1
D'accordo, ma ho visto questo consigliato prima e credo che sia in alcuni standard di programmazione professionale. Stavo commentando di più sul motivo per cui il professore del poster lo aveva escogitato, piuttosto che quando andava bene
Tom Tanner,

2
In seguito a quanto affermato da Pete Becker: non risolve il problema di fondo, ma può mascherarlo. (Ci sono casi in cui dovresti impostare un puntatore NULLdopo averlo eliminato. Se NULLè un valore corretto per il puntatore in tali circostanze; ad esempio, il puntatore punta a un valore memorizzato nella cache e NULLindica una cache non valida. Ma quando vedi qualcuno impostare un puntatore a NULLcome l'ultima riga di un distruttore, ti chiedi se conosce C ++.)
James Kanze,

4

Mi piace la risposta accettata di Luchian, infatti ho imparato a fondo che ha ragione, quindi uso sempre le parentesi graffe, anche per i blocchi a linea singola. Tuttavia, personalmente faccio un'eccezione quando scrivo un filtro, come nel tuo esempio. Questo:

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

mi sembra ingombra. Separa il ciclo for e l'istruzione if in azioni separate, quando in realtà il tuo intento è una singola azione: contare tutti gli interi divisibili per 2. In un linguaggio più espressivo, questo potrebbe essere scritto come:

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

Nelle lingue prive di chiusure, il filtro non può essere espresso in una singola istruzione, ma deve essere un ciclo for seguito da un'istruzione if. Tuttavia, è ancora un'azione nella mente del programmatore, e credo che dovrebbe riflettersi nel codice, in questo modo:

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

7
Mi piace il modo in cui tutti riescono a ignorare for (int i = 0; i < 100; i += 2);, per il bene di continuare l'argomentazione sulla rientranza ;-) Probabilmente c'è un duello completamente separato che potremmo avere, come "meglio" esprimere la logica "per ognuno iin un certo intervallo con una certa proprietà" in C ++ senza loop, usando una combinazione da incubo di algoritmi standard filter_iteratore / o counting_iterator.
Steve Jessop,

1
Inoltre, se avessimo questo, non potremmo essere in disaccordo su come rientrare nell'enorme affermazione singola risultante.
Steve Jessop,

2
@Steve, è solo un esempio però. Ci sono molti usi legittimi del modello. Ovviamente se vuoi contare i numeri da 1 a 100 che sono divisibili per 2, tutto quello che devi fare è 100/2.
MikeFHay,

2
Certo, lo so, è per questo che ho estratto "per ognuno iin un certo intervallo con una certa proprietà". È solo che di solito su SO, le persone sono molto veloci a ignorare la vera domanda a favore di un approccio completamente diverso all'esempio dato. Ma il rientro è importante , quindi non lo facciamo ;-)
Steve Jessop,

4

Un'opzione per aiutare a prevenire gli errori che sono stati descritti in precedenza è quella di includere ciò che vuoi che accada quando non usi le parentesi graffe. Rende molto più difficile non notare gli errori quando si tenta di modificare il codice.

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

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

5
La seconda opzione produrrebbe un errore di compilazione, perché elsenon è associata a un if.
Luchian Grigore,

Un problema non così evidente con inline è che la maggior parte degli IDE predefiniti lo cambiano allo stile rientrato quando si utilizza la loro utilità di formattazione automatica.
Honza Brabec,

@Honza: questa è una questione politica molto carica, però. Se stiamo collaborando su una base di codice, o dobbiamo usare lo stesso stile di rientro fino all'ultimo dettaglio, oppure dobbiamo entrambi concordare di non formattare automaticamente il codice esistente "solo perché". Se il primo, lo stile concordato potrebbe ancora includerlo, ma dovresti configurare l'IDE per rispettarlo o non utilizzare la formattazione automatica. Accettare che il formato comune sia "qualunque sia il mio formato IDE automatico" va benissimo se tutti usiamo lo stesso IDE per sempre, altrimenti non va bene.
Steve Jessop,

3

Se sei un compilatore, non fa alcuna differenza. Entrambi sono uguali.

Ma per i programmatori, il primo è più chiaro, facile da leggere e meno soggetto a errori.


2
Oltre all'apertura {sulla propria linea, comunque.
Rob,

3

Un altro esempio di aggiunta di parentesi graffe. Una volta stavo cercando un bug e ho trovato questo codice:

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

Se leggi il metodo riga per riga noterai che esiste una condizione nel metodo che restituisce se non è vera. Ma in realtà sembra altri 100 semplici gestori di eventi che impostano alcune variabili in base ad alcune condizioni. E un giorno entra in Fast Coder e aggiunge un'ulteriore istruzione di impostazione delle variabili alla fine del metodo:

{
    ...
    OtherwiseSomeAdditionalStatement3;
    SetAnotherVariableUnconditionnaly;
}

Di conseguenza SetAnotherVariableUnconditionnaly viene eseguito quando SomeConditionIsMet (), ma il ragazzo veloce non se ne è accorto perché tutte le linee hanno dimensioni quasi simili e anche quando la condizione di ritorno è rientrata verticalmente non è così evidente.

Se il ritorno condizionale è formattato in questo modo:

if (!SomeConditionIsMet())
{
    return;
}

è molto evidente e il Fast Coder lo troverà a colpo d'occhio.


Se il tuo codificatore veloce non può essere disturbato a individuare returnun'istruzione evidenziata dalla sintassi nel corpo di una funzione prima di aggiungere qualcosa ad essa, non dovresti lasciare che il codificatore veloce si avvicini al tuo codice. Non impedirai a un ragazzo del genere di trollare intorno al tuo codice includendo parentesi graffe.
cmaster - ripristina monica

@cmaster Non lavora più con noi. Ad ogni modo, l'evidenziazione della sintassi è buona, ma ricorda che ci sono persone che non vedono chiaramente (ho visto anche un post di un programmatore cieco l'anno scorso).
Artemix,

2

Considero il primo chiaro, poi il secondo. Dà la sensazione di chiudere le istruzioni, con poco codice va bene quando il codice diventa complesso {...}aiuta molto anche se lo è endifobegin...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

Si consiglia di impostare il puntatore su NULL al termine.

Ecco un esempio del perché:

La classe A procede come segue:

  1. Alloca un blocco di memoria
  2. Quindi qualche tempo dopo, elimina questo blocco di memoria ma non imposta il puntatore su NULL

La classe B procede come segue

  1. Alloca memoria (e in questo caso gli viene dato lo stesso blocco di memoria che è stato cancellato dalla classe A.)

A questo punto sia la Classe A che la Classe B hanno puntatori che puntano allo stesso blocco di memoria, per quanto riguarda la Classe A questo blocco di memoria non esiste perché è finito con esso.

Considera il seguente problema:

E se si fosse verificato un errore logico nella Classe A che lo ha portato a scrivere in memoria che ora appartiene alla Classe B?

In questo caso particolare, non si otterrà un errore di eccezione di accesso errato perché l'indirizzo di memoria è legale, nel frattempo la classe A ora sta effettivamente corrompendo i dati di classe B.

La classe B potrebbe eventualmente arrestarsi in modo anomalo se incontra valori imprevisti e quando si verifica un arresto anomalo, è probabile che trascorrerai molto tempo a cercare questo bug in classe B quando il problema è in classe A.

Se il puntatore della memoria eliminata fosse stato impostato su NULL, si sarebbe verificato un errore di eccezione non appena un errore logico nella classe A avesse tentato di scrivere sul puntatore NULL.

Se sei preoccupato per l'errore logico con la doppia eliminazione quando i puntatori sono NULL per la seconda volta, quindi aggiungi asserzione per questo.

Inoltre: se hai intenzione di votare in basso, spiega.


2
Se si è verificato un errore logico, dovrebbe essere corretto, piuttosto che mascherarlo.
uınbɐɥs

@Barmar, OP dice ... 6. Impostare il puntatore su NULL dopo l'eliminazione - Doppia protezione dalla cancellazione // non male. Alcune persone hanno risposto non impostandolo su Null e sto dicendo perché dovrebbe essere impostato su NULL, quale parte di 6. la mia risposta all'impostazione NULL non rientra in 6?
Giovanni,

1
@Shaquin, E come proponi in primo luogo di trovare questi errori logici? Dopo aver impostato la variabile puntatore su NULL dopo aver eliminato la memoria. Qualsiasi tentativo di fare riferimento al puntatore NULL si arresta in modo anomalo al debugger sulla linea in cui è stato effettuato il tentativo illegale. È possibile risalire indietro e vedere dov'era l'errore logico e risolvere il problema. Se non si imposta la variabile puntatore su NULL dopo aver eliminato la memoria, il tentativo illegale di scrivere questa memoria eliminata a causa di errori logici UNAWARE potrebbe avere esito positivo e pertanto non si blocca in quel punto. Non lo sta mascherando.
Giovanni,

1

Avere sempre parentesi graffe è una regola molto semplice e robusta. Tuttavia, il codice potrebbe apparire non elegante quando ci sono molte parentesi graffe. Se le regole consentono di omettere parentesi graffe, dovrebbero esserci regole di stile più dettagliate e strumenti più sofisticati. Altrimenti può facilmente risultare con codice caotico e confuso (non elegante). Pertanto, la ricerca di una singola regola di stile separata dal resto delle guide di stile e degli strumenti utilizzati è probabilmente inutile. Porterò solo alcuni dettagli importanti su quella regola n. 3 che non sono stati nemmeno menzionati in altre risposte.

Il primo dettaglio interessante è che la maggior parte dei sostenitori di quella regola accetta di violarla in caso di else. In altre parole, non richiedono in revisione tale codice:

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

Invece, se lo vedono, possono anche suggerire di scriverlo in questo modo:

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

Questa è tecnicamente violazione di quella regola poiché non ci sono parentesi graffe tra elsee if. Tale dualità della regola emerge quando provare ad applicarla automaticamente alla base di codice con uno strumento insensato. In effetti, perché discutere, basta lasciare uno strumento per applicare automaticamente lo stile.

Il secondo dettaglio (che viene spesso dimenticato dai sostenitori di quella regola) è che gli errori che possono verificarsi non sono mai solo a causa delle violazioni di quella regola n. 3. In realtà quelli quasi sempre comportano anche violazioni della regola n. 1 (di cui nessuno discute). Sempre dal punto di vista degli strumenti automatici, non è difficile creare uno strumento che si lamenta immediatamente quando viene violata la regola n. 1 e quindi la maggior parte degli errori può essere colta tempestivamente.

Il terzo dettaglio (che viene spesso dimenticato dagli avversari di quella regola) è la natura confusa dell'affermazione vuota che è rappresentata da un singolo punto e virgola. La maggior parte degli sviluppatori con una certa esperienza è stata confusa prima o poi da un solo punto e virgola fuori posto o da un'istruzione vuota che è scritta usando un solo punto e virgola. Due parentesi graffe invece del punto e virgola singolo sono visivamente molto più facili da individuare.


1

Devo ammettere che non sempre si usa {}per la linea singola, ma è una buona pratica.

  • Diciamo che scrivi un codice senza parentesi che assomiglia a questo:

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

E dopo qualche tempo vuoi aggiungere in jloop alcune altre cose e lo fai semplicemente per allineamento e dimentica di aggiungere parentesi.

  • La dislocazione della memoria è più veloce. Supponiamo che tu abbia un grande scopo e crei grandi array all'interno (senza di newche sono in pila). Tali array vengono rimossi dalla memoria subito dopo aver lasciato l'ambito. Ma è possibile che tu usi quell'array in un posto e rimarrà in pila per un po 'e sarà una specie di spazzatura. Poiché una pila ha dimensioni limitate e piuttosto ridotte, è possibile superare la dimensione della pila. Quindi in alcuni casi è meglio scrivere {}per evitarlo. NOTA questo non è per riga singola ma per tali situazioni:

    if (...) {// SomeStuff ... {// non abbiamo if, while, ecc. // SomeOtherStuff} // SomeMoreStuff}

  • Il terzo modo di usarlo è simile al secondo. Semplicemente non per rendere più pulito lo stack ma per aprire alcune funzioni. Se si utilizza mutexin funzioni lunghe di solito è meglio bloccare e sbloccare appena prima di accedere ai dati e subito dopo aver finito di leggerlo / scriverlo. NOTA in questo modo si utilizza se ne hai alcuni classo structcon constructore destructorper bloccare la memoria.

  • Inoltre:

    if (...) if (...) SomeStuff (); altro SomeOtherStuff (); // passa al secondo se, ma l'alligmento mostra che è il primo ...

Tutto sommato, non posso dire quale sia il modo migliore da usare sempre {}per una singola riga, ma non è niente di male farlo.

MODIFICA IMPORTANTE Se scrivi parentesi di codice di compilazione per una singola riga non fa nulla, ma se il tuo codice verrà interpretato, il codice verrà rallentato molto leggermente. Molto leggermente.


1

Esistono diversi modi per scrivere dichiarazioni di controllo; alcune combinazioni possono coesistere senza compromettere la leggibilità, ma altre combinazioni causeranno problemi. Lo stile

if (condition)
  statement;

coesisteranno comodamente con alcuni degli altri modi di scrivere dichiarazioni di controllo, ma non così bene con altri. Se le istruzioni controllate su più righe sono scritte come:

if (condition)
{
  statement;
  statement;
}

allora sarà visivamente ovvio quali ifistruzioni controllano una singola riga e quali controllano più righe. Se, tuttavia, multilineaif istruzioni sono scritte come:

if (condition) {
  statement;
  statement;
}

quindi la probabilità che qualcuno cerchi di estendere una singola affermazione if costrutto a senza aggiungere le parentesi graffe necessarie potrebbe essere molto più alta.

L'istruzione di riga singola sull'istruzione successiva ifpuò anche essere problematica se la base di codice fa un uso significativo del modulo

if (condition) statement;

La mia preferenza è che avere l'istruzione sulla propria linea generalmente migliora la leggibilità, tranne nei casi in cui ci sono molte ifistruzioni con blocchi di controllo simili, ad es.

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

nel qual caso in genere precederò e seguirò tali gruppi di ifistruzioni con una riga vuota per separarli visivamente da altri codici. Avere una serie di affermazioni che iniziano tutte con iflo stesso rientro fornirà quindi una chiara indicazione visiva che c'è qualcosa di insolito.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.