Peggior pratiche in C ++, errori comuni [chiuso]


35

Dopo aver letto questo famoso rant di Linus Torvalds , mi chiedevo quali fossero in realtà tutte le insidie ​​dei programmatori in C ++. Non mi riferisco esplicitamente agli errori di battitura o al flusso di programma errato come trattati in questa domanda e alle sue risposte , ma a errori più di alto livello che non vengono rilevati dal compilatore e non comportano errori evidenti alla prima esecuzione, errori di progettazione completi, cose che sono improbabili in C ma che probabilmente verranno fatte in C ++ dai nuovi arrivati ​​che non comprendono le implicazioni complete del loro codice.

Accolgo con favore anche le risposte sottolineando un enorme calo delle prestazioni dove normalmente non ci si aspetterebbe. Un esempio di ciò che uno dei miei professori una volta mi disse di un generatore di parser LR (1) che scrissi:

Hai usato un numero eccessivo di casi di ereditarietà e virtualità non necessari. L'ereditarietà rende una progettazione molto più complicata (e inefficiente a causa del sottosistema RTTI (inferenza di tipo run-time)) e dovrebbe quindi essere utilizzata solo dove ha senso, ad esempio per le azioni nella tabella di analisi. Poiché fai un uso intensivo dei modelli, praticamente non hai bisogno dell'ereditarietà ".


6
Alcuni degli errori più cattivi che puoi commettere in C / C ++ sono principalmente dovuti all'eredità di C ... leggi comportamento indefinito, gestione manuale della memoria, ecc. Inoltre, i consigli del prof sembrano falsi / sbagliati (per me, che non lo è un esperto C ++) - l'istanza del modello dovrebbe produrre una classe normale con vtable per le virtualfunzioni, giusto?

8
O stai ricordando male quello che ha detto il tuo professore, o non aveva idea di cosa stesse parlando. Le classi derivate generalmente non devono usare RTTI (riflessione AKA) per cercare le cose. Se utilizzano metodi virtuali, potrebbe essere necessario che il codice esegua una ricerca vtable per l'invio, ma ciò si traduce in una singola istruzione ASM su molti processori. A causa di problemi di memorizzazione nella cache, è possibile rallentare le cose di un certo importo, ma è improbabile che si noti mai il sovraccarico in nessuno dei casi d'uso più impegnativi. Ci sono molte buone ragioni per evitare il C ++, ma le ricerche di vtable non sono tra queste.
Mason Wheeler,

5
@FelixDombek: dichiarato in modo così generico e applicato su tutta la linea, quella citazione del tuo professore mostra solo un'enorme quantità di ignoranza. Quando il tuo progetto necessita di un polimorfismo di runtime di qualche tipo, usare le funzioni virtuali è spesso la scelta migliore; quando non ne hai bisogno, non usarlo: non hai bisogno di tutti i metodi per essere virtuale solo perché usi le classi derivate, per esempio.
Fred Nurk,

5
@Mason Wheeler: RTTI contiene informazioni sul tipo, abbastanza per essere in grado di determinare se un dynamic_castdovrebbe avere successo o meno, e poche altre cose, ma la riflessione copre molto di più, inclusa la possibilità di recuperare informazioni su attributi o funzioni dei membri, che non è presente in C ++.
David Rodríguez - dribeas,

5
Il commento del professore è in qualche modo ingannevole, poiché l'ereditarietà e le funzioni virtuali non sono un grande successo per le prestazioni. Il consiglio di usare l'eredità con parsimonia è buono, ma è più un problema di struttura del programma che di efficienza. L'ereditarietà, in particolare con i membri protetti, è un accoppiamento tanto vicino quanto si otterrà, e se non ne hai bisogno non dovresti usarlo.
David Thornley,

Risposte:


69

Torvalds sta parlando a crepapelle qui.


OK, perché sta parlando dal suo culo:

Prima di tutto, il suo sfogo è davvero niente MA irruzione. C'è pochissimo contenuto reale qui. L'unico motivo per cui è davvero famoso o anche leggermente rispettato è perché è stato creato dal dio Linux. Il suo argomento principale è che il C ++ è una schifezza e gli piace far incazzare le persone in C ++. Ovviamente non c'è motivo di rispondere a questo e chiunque lo consideri un argomento ragionevole è comunque fuori discussione.

Quanto a ciò che potrebbe essere brillato come i suoi punti più oggettivi:

  • STL e Boost sono una vera merda <- qualunque cosa. Sei un idiota.
  • STL e Boost causano infinite quantità di dolore <- ridicolo. Ovviamente sta volutamente esagerando, ma qual è la sua vera affermazione qui? Non lo so. Ci sono alcuni problemi più che banalmente difficili da capire quando si causa il vomito del compilatore in Spirit o qualcosa del genere, ma non è più o meno difficile da capire rispetto al debug di UB causato dall'abuso di costrutti C come vuoto *.
  • I modelli astratti incoraggiati dal C ++ sono inefficienti. <- Ti piace cosa? Non si espande mai, non fornisce mai esempi di ciò che intende, lo dice semplicemente. BFD. Dal momento che non posso dire a cosa si riferisca, è inutile cercare di "confutare" l'affermazione. È un mantra comune di C bigotti ma ciò non lo rende più comprensibile o intelligibile.
  • L'uso corretto di C ++ significa limitarsi agli aspetti C. <- In realtà il codice WORSE C ++ disponibile lo fa, quindi non so ancora di che WTF stia parlando.

Fondamentalmente, Torvalds sta parlando dal suo culo. Non c'è argomento comprensibile su nulla. Aspettarsi una seria confutazione di tali sciocchezze è semplicemente sciocco. Mi viene detto di "espandermi" su una confutazione di qualcosa su cui mi sarei aspettato di espandermi se fosse quello in cui l'ho detto. Se davvero, onestamente guardi quello che ha detto Torvalds, vedresti che in realtà non ha detto nulla.

Solo perché Dio dice che non significa che abbia alcun senso o dovrebbe essere preso più seriamente che se un bozo casuale lo dicesse. A dire il vero, Dio è solo un altro bozo casuale.


Rispondere alla domanda effettiva:

Probabilmente la peggiore e più comune, cattiva pratica C ++ è quella di trattarla come C. L'uso continuato delle funzioni dell'API C come printf, diventa (anche considerato cattivo in C), strtok, ecc ... non solo non riesce a sfruttare la potenza fornita dal sistema di tipo più stretto, portano inevitabilmente a ulteriori complicazioni quando si cerca di interagire con il codice C ++ "reale". Quindi, in pratica, fai esattamente l'opposto di ciò che Torvalds sta suggerendo.

Impara a sfruttare STL e Boost per ottenere un ulteriore rilevamento del tempo di compilazione dei bug e per semplificarti la vita in altri modi generali (il tokenizer boost, ad esempio, è sia sicuro da scrivere che un'interfaccia migliore). È vero che dovrai imparare a leggere gli errori del modello, che inizialmente sono scoraggianti, ma (nella mia esperienza comunque) è francamente molto più facile che provare a eseguire il debug di qualcosa che genera un comportamento indefinito durante il runtime, che l'API C fa abbastanza facile da fare.

Per non dire che C non è buono. Ovviamente mi piace il C ++ meglio. Ai programmatori C piace C meglio. Ci sono compromessi e Mi piace soggettivi in ​​gioco. C'è anche molta disinformazione e FUD in giro. Direi che ci sono più FUD e disinformazione in circolazione su C ++, ma sono di parte in questo senso. Ad esempio, i problemi di "gonfiore" e "prestazioni" presumibilmente il C ++ non sono in realtà problemi importanti per la maggior parte del tempo e sono certamente spazzati via dalle proporzioni della realtà.

Per quanto riguarda i problemi a cui si riferisce il tuo professore, questi non sono esclusivi del C ++. In OOP (e nella programmazione generica) preferisci la composizione rispetto all'ereditarietà. L'ereditarietà è la relazione di accoppiamento più forte possibile che esiste in tutte le lingue OO. Il C ++ aggiunge un altro che è più forte, l'amicizia. L'eredità polimorfica dovrebbe essere usata per rappresentare le astrazioni e le relazioni "is-a", non dovrebbe mai essere usata per il riutilizzo. Questo è il secondo errore più grande che puoi fare in C ++, ed è piuttosto grande, ma è tutt'altro che unico nel linguaggio. Puoi creare relazioni di ereditarietà troppo complesse anche in C # o Java e avranno esattamente gli stessi problemi.


1
Ironico, fino a dopo il 2007, git eseguiva solo versioni portabili di Linux. Bene, qualsiasi sistema simile a unix. Poi di nuovo, date le circostanze che hanno portato alla creazione di git, certamente non lo tengo contro di lui.
Chris K,

9
Linus fa fatica a trovare buoni programmatori C ++ che vogliono lavorare per lui. Mi chiedo perché? Penso che questo sia solo un tipo di problema con pollo e uova.
Bo Persson,

19

Ho sempre pensato che i pericoli del C ++ fossero fortemente esagerati dai programmatori C con Classi inesperti.

Sì, C ++ è più difficile da imparare rispetto a qualcosa come Java, ma se programmi usando tecniche moderne è abbastanza facile scrivere programmi robusti. Onestamente non ho che molto più difficile di un tempo di programmazione in C ++ di me in linguaggi come Java, e mi ritrovo spesso manca certe astrazioni C ++, come modelli e RAII Quando disegno in altre lingue.

Detto questo, anche dopo anni di programmazione in C ++, ogni tanto commetterò un errore davvero stupido che non sarebbe possibile in un linguaggio di livello superiore. Una trappola comune in C ++ è ignorare la durata degli oggetti: in Java e C # generalmente non devi preoccuparti della vita degli oggetti *, perché tutti gli oggetti esistono nell'heap e sono gestiti per te da un magico garbage collector.

Ora, nel moderno C ++, di solito non è nemmeno necessario preoccuparsi troppo della vita degli oggetti. Hai distruttori e puntatori intelligenti che gestiscono la durata degli oggetti per te. Il 99% delle volte funziona perfettamente. Ma ogni tanto verrai fregato da un puntatore penzolante (o riferimento). Ad esempio, proprio di recente ho avuto un oggetto (chiamiamolo Foo) che conteneva una variabile di riferimento interna a un altro oggetto (chiamiamolo Bar). A un certo punto, ho sistemato stupidamente le cose in modo che prima Barandassero fuori dal campo di applicazione Foo, eppure Fooil distruttore ha finito per chiamare una funzione membro di Bar. Inutile dire che le cose non sono andate bene.

Ora, non posso davvero dare la colpa a C ++ per questo. Era il mio cattivo design, ma il punto è che questo genere di cose non accadrà in un linguaggio gestito di livello superiore. Anche con puntatori intelligenti e simili, a volte è ancora necessario avere una consapevolezza della vita degli oggetti.


* Se la risorsa gestita è la memoria, ovvero.


8
Non devi mai preoccuparti della durata degli oggetti in Java e C #? Il loro GC si occupa della memoria, ma questa è solo una piccola parte di RAII per me; guarda le varie interfacce "usa e getta" di quelle lingue, per esempio.
Fred Nurk,

Doversi preoccupare della durata degli oggetti sarebbe raro in Java, tranne per la scomoda progettazione della sua libreria I / O.
dan04,

Il tuo problema di riferimento penzolante è qualcosa che mi interessa cercare di risolvere. Ho iniziato una discussione sul mio blog sulla direzione in cui sono diretto a risolverlo (promesse del puntatore). Fondamentalmente penso che il linguaggio potrebbe usare qualche altro suggerimento intelligente. Partecipa a quella discussione se sei interessato. Nessun altro è stato così ... ma se è qualcosa che ti piacerebbe vedere risolto ... In realtà ho riscontrato il problema molto più del 10% delle volte.
Edward Strange,

13

La differenza nel codice è generalmente più correlata al programmatore che alla lingua. In particolare, un buon programmatore C ++ e un programmatore C arriveranno entrambi a soluzioni altrettanto buone (anche se diverse). Ora, C è un linguaggio più semplice (come linguaggio) e ciò significa che ci sono meno astrazioni e maggiore visibilità su ciò che il codice effettivamente fa.

Una parte del suo sfogo (è noto per i suoi risentimenti contro il C ++) si basa sul fatto che più persone prenderanno il C ++ e scriveranno il codice senza realmente capire ciò che alcune astrazioni nascondono e fanno ipotesi errate.


3
Qual è il costo dell'iterazione rispetto alla std::vector<bool>modifica di ciascun valore? for ( std::vector<bool>::iterator it = v.begin(), end = v.end(); it != end; ++it ) { *it = !*it; }? Cosa viene sottratto *it = !*it;?
David Rodríguez - dribeas,

2
Anche se può essere ingiusto scegliere abominazioni linguistiche specifiche ampiamente criticate come errori ...
Fred Nurk,

2
@Fred Nurk: std::vector<bool>è un errore ben noto, ma è un ottimo esempio di ciò che si sta discutendo: le astrazioni sono buone, ma devi stare attento a ciò che nascondono. Lo stesso può e accadrà nel codice utente. Per i principianti, ho visto sia in C ++ che in Java utilizzare le eccezioni per eseguire il controllo del flusso e codice che assomiglia a una chiamata di funzione di annidamento che in realtà è un lanciatore di eccezioni di salvataggio: void endOperation();implementato come throw EndOperation;. Un buon programmatore eviterà quei costrutti sorprendenti , ma il fatto è che puoi trovarli.
David Rodríguez - dribeas,

5
Uno dei punti di Torvalds è questo: può allontanare i principianti semplicemente scegliendo C su C ++ (sembra che ci siano più principianti C ++) e C ++ essendo più complesso ha una curva di apprendimento più ripida e ci sono più possibilità di inciampare in un caso d'angolo .
David Rodríguez - dribeas,

2
+1, questo è esattamente ciò di cui si lamenta Linus. Viene fuori come anti-C ++, ma non è proprio così. È solo anti-C ++ - programmatore.
Greyfade,

13

Uso eccessivo di try/catchblocchi.

File file("some.txt");
try
{
  /**/

  file.close();
}
catch(std::exception const& e)
{
  file.close();
}

Questo di solito deriva da linguaggi come Java e la gente sosterrà che al C ++ manca una finalizeclausola.

Ma questo codice presenta due problemi:

  • È necessario compilare fileprima di try/catch, perché non è possibile effettivamente closeun file in cui non esiste catch. Ciò porta a una "perdita di portata" che fileè visibile dopo essere stata chiusa. Puoi aggiungere un blocco ma ...: /
  • Se qualcuno si avvicina e aggiunge a returnnel mezzo trydell'ambito, il file non viene chiuso (motivo per cui le persone si lamentano della mancanza di finalizeclausola)

Tuttavia, in C ++, abbiamo modi molto più efficienti di affrontare questo problema che:

  • Java finalize
  • C # s ' using
  • Go defer

Abbiamo RAII, la cui proprietà davvero interessante è meglio sintetizzata come SBRM(Scoped Bound Resources Management).

Creando la classe in modo che il suo distruttore ripulisca le risorse che possiede, non ci impegniamo a gestire la risorsa su ognuno dei suoi utenti!

Questa è la caratteristica che mi manca in qualsiasi altra lingua, e probabilmente quella che è più dimenticata.

La verità è che raramente è necessario persino scrivere un try/catchblocco in C ++, a parte al livello più alto per evitare la chiusura senza registrazione.


1
Non penso che sia l'influenza di Java tanto quanto è C. (si potrebbe sostituire direttamente fopene fclosequi.) RAII è il modo "corretto" di fare le cose qui, ma è scomodo per le persone che vogliono usare le librerie C da C ++ .
dan04

Per questo tipo di risposta, fornire un esempio della soluzione corretta sarebbe stato appropriato.
Claus Jørgensen,

@ ClausJørgensen: Beh, purtroppo la soluzione non è davvero "appariscente" poiché coinvolge solo File file("some.txt");e basta (no open, no close, no try...)
Matthieu M.

D ha anche RAII
Demi il

@Demetri: non ho troppa familiarità con D, potresti spiegare come RAII interagisce con Garbage Collection? So che in Python puoi scrivere un metodo "deinit", tuttavia la documentazione avverte che in caso di ciclo di riferimenti alcuni oggetti non vedranno il loro metodo deinit chiamato.
Matthieu M.

9

Un errore comune che si adatta ai tuoi criteri non è capire come funzionano i costruttori di copie quando si tratta di memoria allocata nella tua classe. Ho perso il conto del tempo che ho trascorso a riparare crash o perdite di memoria perché un "noob" ha messo i loro oggetti in una mappa o in un vettore e non ha scritto correttamente i costruttori di copie e i distruttori.

Sfortunatamente il C ++ è pieno di gotcha "nascosti" come questo. Ma lamentarti è come lamentarti del fatto che sei andato in Francia e non hai capito cosa dicevano le persone. Se andrai lì, impara la lingua.


1
Penso che il problema con il C ++ sia che è molto facile spararsi al piede. Certo, ci sono buoni programmatori C ++ in giro, molti buoni software scritti in C ++. Ma è molto difficile diventare un buon sviluppatore C ++. La serie "Efficient C ++" di Scott Meyers mostra quante sottigliezze ha il linguaggio.
Marco Mustapic,

Sono d'accordo. Parte del problema però è che molti (la maggior parte) dei programmatori C ++ pensano di sapere cosa stanno facendo quando chiaramente non lo fanno. Intendevi "C ++ efficace"?
Henry,

Almeno questo sta migliorando con le nuove regole piuttosto restrittive sulla generazione implicita di operazioni di copia / spostamento in C ++ 0x. In molti dei casi di violazione della regola dei tre la generazione implicita di operazioni di copia sarà deprecata e dovrebbe generare un avviso.
sellibitze,

6

Il C ++ consente una grande varietà di funzionalità e stili di programmazione, ma ciò non significa che questi siano in realtà buoni modi per utilizzare C ++. E infatti, è incredibilmente facile usare C ++ in modo errato.

Deve essere appreso e compreso correttamente , semplicemente apprendere facendo (o usandolo come si userebbe un'altra lingua) porterà a un codice inefficace e soggetto a errori.


4

Bene ... Per cominciare puoi leggere le Domande frequenti Lite C ++

Quindi, diverse persone hanno costruito carriere scrivendo libri sulle complessità del C ++:

Herb Sutter e Scott Meyers in particolare.

Per quanto riguarda il rant di Torvalds che manca di sostanza ... dai, sul serio: nessun altra lingua là fuori ha versato così tanto inchiostro nel trattare le sfumature della lingua. I tuoi libri Python & Ruby e Java si concentrano tutti sulla scrittura di applicazioni ... i tuoi libri C ++ si concentrano su funzioni / suggerimenti / trappole per il linguaggio sciocco.


1
Hmm ... javapuzzlers.com , jimbrooks.org/web/python/#Pitfalls . Direi che il C ++ accelerato (per un esempio) si concentra molto di più su come scrivere codice di quanto non facciano questi ...
Jerry Coffin,

1
Hai raccolto un paio di esempi di risorse che indicano casi limite nelle loro rispettive lingue; cose che sembrano strane e non sei del tutto sicuro di come funzionerebbero (anche se le cose della lista di Python sono vicine) ... C ++ ha un intero settore che sottolinea cose che sembrano perfettamente valide che si comportano in modi che non ti aspetti.
terra rossa

3

Un modello troppo pesante potrebbe non causare inizialmente errori. Col passare del tempo, tuttavia, le persone dovranno modificare quel codice e avranno difficoltà a comprendere un modello enorme. Questo è quando i bug entrano: l'incomprensione causa commenti "Compila ed esegue", che spesso portano a un codice quasi ma non del tutto corretto.

In genere, se mi vedo fare un modello generico profondo a tre livelli, mi fermo e penso a come ridurlo a uno. Spesso il problema viene risolto estraendo funzioni o classi.


8
Mantenere un codice complicato a fronte del cambiamento dei requisiti provoca sempre bug senza troppi sforzi, nulla di particolarmente speciale sui modelli lì.
Fred Nurk,

2

Avvertenza: questa non è una risposta tanto quanto una critica alla conversazione a cui "unkk user" ha collegato la sua risposta.

Il suo primo punto principale è il (presumibilmente) "standard in continua evoluzione". In realtà, gli esempi da lui forniti riguardano tutti i cambiamenti in C ++ prima che esistesse uno standard. Dal 1998 (quando è stato finalizzato il primo standard C ++) le modifiche al linguaggio sono state abbastanza minime - in effetti, molti direbbero che il vero problema è che avrebbero dovuto essere apportate ulteriori modifiche. Sono ragionevolmente certo che tutto il codice conforme allo standard C ++ originale sia ancora conforme allo standard attuale. Anche se è un po ' meno certo, a meno che qualcosa non cambi rapidamente (e abbastanza inaspettatamente) lo stesso sarà praticamente vero anche con il prossimo standard C ++ (teoricamente, tutto il codice che ha usatoexportsi romperà, ma praticamente non esiste nessuno; da un punto di vista pratico non è un problema). Mi vengono in mente poche altre lingue, sistemi operativi (o gran parte di qualsiasi altra cosa relativa al computer) in grado di presentare una simile richiesta.

Passa quindi a "stili in continua evoluzione". Ancora una volta, la maggior parte dei suoi punti sono abbastanza vicini alle sciocchezze. Cerca di caratterizzarsi for (int i=0; i<n;i++)come "vecchio e sballato" e for (int i(0); i!=n;++i)"nuovo calore". La realtà è che mentre ci sono tipi per i quali tali cambiamenti potrebbero avere un senso, per int, non fa alcuna differenza - e anche quando potresti ottenere qualcosa, è raramente necessario per scrivere codice buono o corretto. Anche nella migliore delle ipotesi, sta costruendo una montagna da una talpa.

La sua prossima affermazione è che C ++ sta "ottimizzando nella direzione sbagliata" - in particolare, mentre ammette che usare buone librerie è facile, afferma che C ++ "rende quasi impossibile scrivere buone librerie". Qui, credo sia uno dei suoi errori più fondamentali. In realtà, scrivere buone librerie per quasi tutte le lingue è estremamente difficile. Come minimo, per scrivere una buona libreria è necessario comprendere un dominio del problema così bene che il codice funziona per una moltitudine di possibili applicazioni in (o relative a) quel dominio. La maggior parte di ciò che C ++ realmente fa è "alzare il tiro" - dopo aver visto quanto di meglio una biblioteca può essere, le persone sono raramente disposti a tornare a scrivere il tipo di dreck avrebbero altrimenti.programmatori davvero bravi scrivono parecchie librerie, che possono quindi essere usate (facilmente, come ammette) da "il resto di noi". Questo è davvero un caso in cui "non è un bug, è una funzionalità".

Non proverò a centrare tutti i punti in ordine (ciò richiederebbe delle pagine), ma salterò direttamente al suo punto di chiusura. Cita Bjarne dicendo: "L'ottimizzazione dell'intero programma può essere utilizzata per eliminare tabelle di funzioni virtuali inutilizzate e dati RTTI. Tale analisi è particolarmente adatta per programmi relativamente piccoli che non utilizzano collegamenti dinamici".

Lo critica affermando senza mezzi termini che "Questo è un problema davvero difficile", arrivando persino a confrontarlo con il problema dell'arresto. In realtà, non è niente del genere - in effetti, il linker incluso con Zortech C ++ (praticamente il primo compilatore C ++ per MS-DOS, negli anni '80) lo ha fatto. È vero che è difficile essere certi che tutti i dati eventualmente estranei siano stati eliminati, ma è comunque del tutto ragionevole fare un lavoro abbastanza giusto.

Indipendentemente da ciò, tuttavia, il punto molto più importante è che questo è assolutamente irrilevante per la maggior parte dei programmatori in ogni caso. Come sanno quelli di noi che hanno smontato un po 'di codice, a meno che non si scriva un linguaggio assembly senza librerie, i file eseguibili contengono quasi sicuramente una discreta quantità di "elementi" (sia codice che dati, in casi tipici) che si probabilmente non ne sono nemmeno a conoscenza, per non parlare del fatto che in realtà venga utilizzato. Per la maggior parte delle persone, il più delle volte, non importa, a meno che non si stia sviluppando per i sistemi embedded più piccoli, il consumo aggiuntivo di spazio di archiviazione sia semplicemente irrilevante.

Alla fine, è vero che questo sfogo ha un po 'più di sostanza dell'idiozia di Linus - ma questo gli dà esattamente il danno con deboli elogi che merita.


1

Come programmatore C che ha dovuto programmare in C ++ a causa di circostanze inevitabili, ecco la mia esperienza. Ci sono pochissime cose che uso in C ++ e per lo più attenersi a C. Il motivo principale è perché non capisco bene C ++. Non avevo / non avevo un mentore per mostrarmi le complessità del C ++ e come scrivere un buon codice in esso. E senza la guida di un ottimo codice C ++, è estremamente difficile scrivere un buon codice in C ++. IMHO questo è il più grande svantaggio del C ++ perché è difficile trovare buoni programmatori C ++ disposti a gestire i principianti.

Alcuni dei successi che ho visto di solito sono dovuti alla magica allocazione di memoria di STL (sì, puoi cambiare l'allocatore, ma chi lo fa quando inizia con C ++?). Di solito ascoltate gli argomenti degli esperti di C ++ secondo cui i vettori e gli array offrono prestazioni simili, perché i vettori usano gli array internamente e l'astrazione è super efficiente. Ho scoperto che ciò è vero nella pratica per l'accesso ai vettori e la modifica dei valori esistenti. Ma non è vero per l'aggiunta di una nuova voce, costruzione e distruzione di vettori. gprof ha mostrato che cumulativamente il 25% del tempo per un'applicazione è stato impiegato in costruttori di vettori, distruttori, memmove (per il trasferimento di interi vettori per l'aggiunta di nuovi elementi) e altri operatori vettoriali sovraccarichi (come ++).

Nella stessa applicazione, il vettore di qualcosa di piccolo è stato usato per rappresentare qualcosa di grosso. Non c'era bisogno di un accesso casuale a qualcosa di piccolo in qualcosa di grosso. È stato comunque utilizzato un vettore anziché un elenco. Il motivo per cui è stato utilizzato il vettore? Perché il programmatore originale aveva familiarità con la sintassi dei vettori come l'array e non aveva molta familiarità con gli iteratori necessari per gli elenchi (sì, viene da uno sfondo C). Continua a dimostrare che sono necessarie molte indicazioni da parte di esperti per ottenere il C ++ giusto. C offre così pochi costrutti di base senza alcuna astrazione, che puoi ottenerlo molto più facilmente di C ++.



0

STL e boost sono portatili, a livello di codice sorgente. Suppongo che Linus stia parlando del fatto che al C ++ manca un ABI (interfaccia binaria dell'applicazione). Quindi è necessario compilare tutte le librerie con cui ci si collega, con la stessa versione del compilatore e con gli stessi switch, oppure limitarsi alla C ABI ai limiti della DLL. Trovo anche che annyoing .. ma a meno che tu non stia creando librerie di terze parti, dovresti essere in grado di assumere il controllo del tuo ambiente di compilazione. Trovo che limitarmi alla C ABI non valga la pena. La comodità di poter passare stringhe, vettori e puntatori intelligenti da una DLL all'altra vale la pena di dover ricostruire tutte le librerie quando si aggiornano i compilatori o si cambiano le opzioni del compilatore. Le regole d'oro che seguo sono:

-Eredita per riutilizzare l'interfaccia, non l'implementazione

-Preferire l'aggregazione rispetto all'eredità

-Preferire ove possibile funzioni gratuite ai metodi membro

-Usare sempre il linguaggio RAII per rendere il codice fortemente sicuro. Evita di provare a catturare.

-Utilizzare puntatori intelligenti, evitare puntatori nudi (non di proprietà)

-Preferire la semantica del valore per fare riferimento alla semantica

-Non reinventare la ruota, usare stl e boost

-Utilizzare il linguaggio Pimpl per nascondere privato e / o fornire un firewall del compilatore


-6

Non mettere un finale ;alla fine di una dichiarazione di clase, almeno in alcune versioni di VC.


4
Questo è forse un errore molto comune per i principianti (come è quasi tutto per qualcuno che sta ancora imparando la sintassi di base), ma ci sono molti che si definiscono competenti e trovano ancora questo errore degno di nota?
Fred Nurk,

1
L'ho scritto solo perché il compilatore ti ha dato un errore che non aveva nulla a che fare con la mancanza di un punto e virgola.
Marco Mustapic,

2
Sì, lo stesso errore esatto è quello che ottieni da un compilatore C.
Mircea Chirea,
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.