2009-04-24 6 views
8

Cosa ne pensi delle funzioni di una linea? È male? Un vantaggio che posso pensare è che rende il codice più completo (se si sceglie un buon nome per questo). Per esempio:Una riga funziona in C?

void addint(Set *S, int n) 
{ 
    (*S)[n/CHAR_SIZE] |= (unsigned char) pow(2, (CHAR_SIZE - 1) - (n % CHAR_SIZE)); 
} 

Uno svantaggio che posso pensare è che rallenta il codice (spingendo i parametri di impilare, saltando a una funzione, spuntando i parametri, facendo l'operazione, saltando indietro al codice - e solo per una riga?)

è meglio mettere tali linee in funzioni o semplicemente inserirle nel codice? Anche se li usiamo solo una volta?

BTW, non ho trovato alcuna domanda al riguardo, quindi perdonami se tale domanda è stata posta prima.

+6

Ti stai preoccupando del sovraccarico della chiamata, ma poi chiama Pow per aumentare 2 alla potenza di un numero intero piccolo? ;-p –

+0

Quale potrebbe essere un modo migliore per farlo? Mi piacerebbe saperlo –

+2

2^x è uguale a 2 << x se x è un numero intero –

risposta

21

Non avere paura delle funzioni a 1 riga!

Un sacco di programmatori sembrano avere un blocco mentale sulle funzioni a 1 riga, non si dovrebbe.

Se rende il codice più chiaro e pulito, estrae la linea in una funzione.

Le prestazioni probabilmente non saranno influenzate.

Qualsiasi compilatore decente realizzato nell'ultimo decennio (e forse ulteriormente) incorpora automaticamente una semplice funzione a 1 riga. Inoltre, 1 riga di C può facilmente corrispondere a molte linee di codice macchina. Non bisogna presumere che anche nel caso teorico in cui si incorre nel sovraccarico totale di una chiamata di funzione, questo overhead è significativo rispetto alla "piccola linea". Per non parlare delle prestazioni generali della tua applicazione.

L'astrazione porta a un design migliore. (Anche per le singole righe di codice)

Le funzioni sono gli elementi costitutivi principali del codice astratto e componente, non devono essere trascurate. Se incapsulare una singola riga di codice dietro una chiamata di funzione rende il codice più leggibile, fallo. Anche nel caso in cui la funzione viene chiamata una volta. Se ritieni che sia importante commentare una particolare riga di codice, è un buon odore di codice che potrebbe essere utile spostare il codice in una funzione ben denominata.

Certo, quel codice potrebbe essere 1-riga oggi, ma quanti modi diversi di eseguire la stessa funzione ci sono? L'incapsulamento del codice all'interno di una funzione può rendere più semplice la visualizzazione di tutte le opzioni di progettazione disponibili. Forse la tua 1 riga di codice si espande in una chiamata a un webservice, forse diventa una query di database, forse diventa configurabile (usando il modello di strategia, per esempio), forse vuoi passare alla memorizzazione nella cache del valore calcolato dal tuo 1- linea. Tutte queste opzioni sono più facili da implementare e più facilmente pensate quando hai estratto la tua 1 linea di codice nella sua funzione.

Forse la tua linea deve essere più linee.

Se si dispone di un grande blocco di codice, si può essere tentati di stipare un sacco di funzionalità su una singola riga, solo per salvare sullo schermo immobiliare. Quando si esegue la migrazione di questo codice a una funzione, si riducono queste pressioni, il che potrebbe renderti più incline ad espandere il tuo complesso 1-liner in un codice più semplice che occupa più righe (che probabilmente migliorerebbe la leggibilità e la manutenibilità).

0

Se si utilizza il codice all'interno di tale funzione per 3 o più volte, si consiglia di inserirlo in una funzione. Solo per manutenibilità.

5

Se usato più di una volta, sicuramente lo rende una funzione, e lascia che il compilatore esegua l'inlining (possibilmente aggiungendo "in linea" alla definizione della funzione). (< I consigli abituali sull'ottimizzazione prematura vanno qui >)

0

Quale lingua? Se intendi C, utilizzerei anche il qualificatore inline. In C++, ho l'opzione di inline, boost.lamda o il supporto nativo C++ 0x in avanti per lamdas.

5

Dal momento che l'esempio sembra utilizzare una sintassi C (++), è possibile leggere il documento inline functions che elimina l'overhead della chiamata a una funzione semplice. Questa parola chiave è tuttavia solo una raccomandazione al compilatore e potrebbe non incorporare tutte le funzioni che contrassegni, e potrebbe scegliere di incorporare funzioni non contrassegnate.

In .NET la JIT inline i metodi che ritiene sia appropriato, ma non si ha il controllo sul perché o quando lo fa, anche se (a quanto ho capito) le build di debug non saranno mai in linea poiché ciò fermerebbe il codice sorgente corrispondente all'applicazione compilata.

8

Non sono un fan di avere tutti i tipi di logica e funzionalità sbattuti in una sola riga. L'esempio che hai mostrato è un casino e potrebbe essere scomposto in più righe, usando nomi di variabili significativi ed eseguendo un'operazione dopo l'altra.

ho consiglia vivamente, in ogni domanda di questo tipo, per avere uno sguardo (comprare, prendere in prestito, (non) scaricarlo (gratuitamente)) a this book: Robert C. Martin - Clean Code. È un libro che ogni sviluppatore dovrebbe dare un'occhiata.

Non ti renderà subito un buon programmatore e non ti impedirà di scrivere codice brutto in futuro, ma ti farà comunque capire quando stai scrivendo codice brutto. Ti costringerà a guardare il tuo codice con un occhio più critico ea rendere leggibile il tuo codice come una storia di un giornale.

1

Non c'è niente di sbagliato con le funzioni di una linea. Come accennato, è possibile che il compilatore integri le funzioni che rimuoveranno qualsiasi penalità legata alle prestazioni.

Le funzioni devono essere preferite anche su macro in quanto sono più facili da eseguire il debug, modificare, leggere e con meno probabilità di avere effetti collaterali indesiderati.

Se viene utilizzato solo una volta, la risposta è meno ovvia.Spostarlo su una funzione può rendere più semplice la funzione di chiamata & spostando parte della complessità nella nuova funzione.

-1

A volte non è una cattiva idea di utilizzare il preprocessore:

#define addint(S, n) (*S)[n/CHAR_SIZE] |= (unsigned char) pow(2, (CHAR_SIZE - 1) - (n % CHAR_SIZE)); 

scontato, non si ottiene alcun tipo di controllo di tipo, ma in alcuni casi questo può essere utile. Le macro hanno i loro svantaggi e i loro vantaggi e in alcuni casi i loro svantaggi possono diventare vantaggi. Sono un fan delle macro nei luoghi appropriati, ma spetta a te decidere quando è appropriato. In questo caso, uscirò su un arto e dirò che, qualunque cosa tu finisca, quella riga di codice è piuttosto un po '.

#define addint(S, n) do { \ 
    unsigned char c = pow(2, (CHAR_SIZE -1) - (n % CHAR_SIZE)); \ 
    (*S)[n/CHAR_SIZE] |= c \ 
    } while(0) 
+2

Rispetto al preprocessore: "Abuso da me, Abuso da me, Fammi sentire desiderato!" –

+1

Ho appena sentito che dovrebbe essere menzionato per completezza. Potrebbe non essere la soluzione migliore in questo caso, perché non c'è controllo di tipo, ma lo dico. Non sto dicendo "(ab) usa il preprocessore", sto dicendo "non dimenticare il preprocessore". Non è così male come uno strumento. –

+0

Concordo sul fatto che il preprocessore è uno strumento molto utile. Tuttavia, quando qualcosa diventa così complesso, è spesso meglio metterlo in linea, guardare la discarica asm e vedere se il compilatore è d'accordo. 8/10 volte, il compilatore sarà corretto. Ora, se il tuo compilatore è solo un buco A ** e tu lo sai, rimane il preprocessore. –