Alcune funzionalità linguistiche, anche se sono aggiunte, possono cambiare completamente il modo in cui la lingua deve essere praticamente utilizzata. Ad esempio, considera questo caso:
lock_mutex(&mutex);
// call some functions
...
unlock_mutex(&mutex);
Se quel codice sopra riguardava le funzioni di chiamata implementate in C ++, potremmo trovarci in un mondo di problemi, dato che una di quelle chiamate di funzione potrebbe lanciare e non sbloccheremo mai il mutex in quei percorsi eccezionali.
I distruttori non sono più nel regno della convenienza per aiutare i programmatori a evitare di dimenticare di liberare / liberare risorse a quel punto. RAII diventa un requisito pratico perché non è umanamente possibile anticipare ogni singola riga di codice che può essere lanciata in esempi non banali (per non parlare del fatto che quelle linee potrebbero non essere lanciate ora ma potrebbero in seguito con modifiche). Prendi un altro esempio:
void f(const Foo* f1)
{
Foo f2;
memcpy(&f2, f1, sizeof f2);
...
}
Tale codice, sebbene generalmente innocuo in C, è come il caos infernale che regna in C ++, perché il memcpy
bulldoz su bit e byte di questi oggetti e ignora cose come i costruttori di copie. Tali funzioni gradiscono memset
, realloc
, memcpy
, ecc, mentre gli strumenti giornalieri tra gli sviluppatori C usati per guardare le cose in modo piuttosto omogeneo di bit e byte di memoria, non sono in armonia con il più complesso e più ricco sistema di tipo C ++. Il C ++ incoraggia una visione molto più astratta dei tipi definiti dall'utente.
Quindi questi tipi di cose non consentono più al C ++, da parte di chiunque cerchi di usarlo correttamente, di considerarlo come un semplice "superset" di C. Questi linguaggi richiedono una mentalità, una disciplina e un modo di pensare molto diversi per usare nel modo più efficace .
Non sono nel campo che vede C ++ come decisamente migliore in ogni modo, e in realtà la maggior parte delle mie librerie di terze parti preferite sono librerie C per qualche motivo. Non so perché esattamente, ma le librerie C tendono ad essere di natura più minimalista (forse perché l'assenza di un sistema di tipo così ricco rende gli sviluppatori più concentrati nel fornire le funzionalità minime richieste senza costruire un insieme ampio e stratificato di astrazioni), anche se spesso finisco semplicemente per mettere i wrapper C ++ attorno a loro per semplificare e personalizzare il loro utilizzo per i miei scopi, ma quella natura minimalista è preferibile per me anche quando lo faccio. Adoro il minimalismo come una caratteristica interessante di una biblioteca per coloro che si prendono il tempo extra per cercare tali qualità, e forse C tende a incoraggiarlo,
Preferisco il C ++ molto più spesso, ma in realtà mi viene richiesto di utilizzare le API C piuttosto spesso per la più ampia compatibilità binaria (e per le FFI), anche se spesso le implemento in C ++ nonostante l'uso di C per le intestazioni. Ma a volte quando si passa a un livello molto basso, come il livello di un allocatore di memoria o una struttura di dati di livello molto basso (e sono sicuro che ci sono ulteriori esempi tra coloro che eseguono la programmazione integrata), a volte può essere utile essere in grado di presumere che i tipi e i dati con cui stai lavorando siano assenti alcune funzionalità come vtables, costruttori e distruttori, in modo che possiamo trattarli come bit e byte da mescolare, copiare, liberare, riallocare. Per problemi di livello particolarmente basso, a volte può essere utile lavorare con un sistema di tipo molto più semplice fornito da C,
Un chiarimento
Un commento interessante qui ho voluto rispondere un po 'più in profondità (trovo che i commenti qui siano così severi sul limite di caratteri):
memcpy(&f2, f1, sizeof f2);
è anche "il caos infernale che regna" in C se Foo ha qualche indicatore proprietario, o è anche peggio, poiché mancano anche gli strumenti per affrontarlo.
Questo è un punto giusto, ma tutto ciò su cui mi sto concentrando è principalmente focalizzato sul sistema di tipi di C ++ e anche rispetto a RAII. Uno dei motivi per cui la copia a byte di raggi X memcpy
o qsort
tipi di funzioni rappresentano un pericolo pratico in C è che la distruzione di f1
e f2
sopra è esplicita (se hanno bisogno anche di una distruzione non banale), mentre quando i distruttori entrano in scena , diventano impliciti e automatizzati (spesso con un grande valore per gli sviluppatori). Questo non vuol dire nemmeno lo stato nascosto come vptrs e così via che tali funzioni dovrebbero abbattere proprio sopra. Se f1
possiede puntatori ef2
shallow li copia in un contesto temporaneo, quindi non crea problemi se non proviamo a liberare esplicitamente quei puntatori proprietari una seconda volta. Con C ++ questo è qualcosa che il compilatore vorrà fare automaticamente.
E questo diventa più grande se in genere in C, " Se Foo ha i propri puntatori", perché l'esplicitazione richiesta con la gestione delle risorse renderà spesso qualcosa che in genere è più difficile da trascurare mentre in C ++, possiamo fare un UDT non più banalmente costruibile / distruttibile semplicemente facendo in modo che memorizzi qualsiasi variabile membro che non sia banalmente costruibile / distruttibile (in un modo che è generalmente molto utile, ancora una volta, ma non se siamo tentati di usare funzioni come memcpy
o realloc
).
Il mio punto principale non è quello di cercare di argomentare alcun beneficio di questa esplicitazione (direi che se ce ne sono, sono quasi sempre appesantiti dai contro della maggiore probabilità di errore umano che ne deriva), ma semplicemente per dire che funziona come memcpy
e memmove
e qsort
e memset
e erealloc
e così via non hanno spazio in un linguaggio con UDT ricchi di caratteristiche e capacità come C ++. Sebbene esistano a prescindere, penso che non sarebbe troppo contestato affermare che la stragrande maggioranza degli sviluppatori di C ++ sarebbe saggia per evitare tali funzioni come la peste, mentre queste sono una specie di funzioni quotidiane in C, e io ' d sostengono che pongono meno problemi in C per la semplice ragione che il suo sistema di tipi è molto più semplice e, forse, "più stupido". La radiografia dei tipi C e il loro trattamento come bit e byte è soggetto a errori. Farlo in C ++ è senza dubbio del tutto errato perché tali funzioni stanno combattendo contro caratteristiche fondamentali del linguaggio e ciò che incoraggia il sistema dei tipi.
Questo è in realtà il più grande appello a me di C, tuttavia, in particolare con il modo in cui si riferisce all'interoperabilità del linguaggio. Sarebbe molto, molto più difficile far capire a qualcosa come FFI di C # il sistema di tipo completo e le caratteristiche del linguaggio di C ++ fino a costruttori, distruttori, eccezioni, funzioni virtuali, sovraccarico di funzione / metodo, sovraccarico dell'operatore, tutti i vari tipi di ereditarietà, ecc. Con C è un linguaggio relativamente più stupido che è diventato piuttosto standard per quanto riguarda le API in modi che molte lingue diverse possono importare direttamente tramite FFI o indirettamente tramite alcune funzioni di esportazione dell'API C nella forma desiderata (es: Java Native Interface ). Ed è qui che per la maggior parte mi rimane solo la possibilità di usare C, dal momento che l'interoperabilità del linguaggio è un requisito pratico nel nostro caso (anche se spesso
Ma sai, sono un pragmatico (o almeno mi sforzo di esserlo). Se C fosse il linguaggio più disgustoso e sfrontato, soggetto a errori e brutto che alcuni dei miei coetanei appassionati di C ++ sostenevano che fosse (e mi considererei un appassionato di C ++ solo che in qualche modo non ha portato a un odio di C da parte mia ; al contrario, ha avuto l'effetto opposto su di me di farmi apprezzare meglio entrambe le lingue nei loro aspetti e differenze), quindi mi aspetto che si presenti nel mondo reale sotto forma di alcuni dei più attivi e leali e prodotti e biblioteche inaffidabili scritti in C. E non lo trovo. Mi piace Linux, mi piace Apache, Lua, zlib, trovo OpenGL tollerabile per la sua lunga eredità contro tali requisiti hardware mutevoli, Gimp, libpng, Cairo, ecc. Almeno qualunque ostacolo la lingua sembri non sembra rappresentare un problema per quanto riguarda la scrittura di biblioteche e prodotti interessanti in mani competenti, ed è davvero tutto ciò a cui sono interessato. Quindi non sono mai stato il tipo così interessato ai più appassionati guerre linguistiche, tranne per fare un appello pragmatico e dire: "Ehi, ci sono cose interessanti là fuori! Impariamo come ce l'hanno fatta e forse ci sono lezioni interessanti, non così specifiche per la natura idiomatica della lingua, che possiamo riportare indietro in qualsiasi lingua (e) che stiamo usando. " :-D