mercoledì 13 aprile 2011

C++ e C# a confronto

Molto spesso si è portati a pensare che C#, il linguaggio "di punta" del framework .NET, sia un C++ "migliore", un "C++++" come suggerisce l'immagine, in realtà le differenze sono profonde, più che da un punto di vista sintattico, da un punto di vista concettuale. 
Vediamo di seguito una tabella sulle maggiori differenze riguardo i due linguaggi.

C++
C#
I riferimenti ad un’altra classe in un altro file del progetto richiedono un #include
Nessun #include è richiesto
I riferimenti ad una classe in una libreria dinamica (dll) richiedono di includere un file e il link ad una libreria statica (lib)
Non ci sono più le librerie statiche. Si possono aggiungere i riferimenti alle dll (assemblies) direttamente da progetto
I simboli esportati da una libreria devono essere dichiarati come declspec o in un .DEF file
Basta dichiarare i simboli come pubblici nella libreria. I simboli privati non vengono esportati.
Se ci si riferisce ad una classe/struttura prima che venga dichiarata, bisogna fare una forward declaration
Non esistono le forward declarations e l’ordine di dichiarazione non è importante
Passando tra stringhe Unicode/16-bit si richiede:
  • entry point differenti (main/wmain)
  • Prefissi non standard “L” or “_T”, oppure gestione STL con string/wstring
  • Le stringhe vengono manipolate da differenti funzioni
Unicode è il formato nativo
NULL è un concetto, non fa parte del linguaggio. È tipicamente definito come 0 e quindi è l'equivalente di 0.
null è una parola chiave. Non è equivalente a 0.
I tipi di dato base (int, float, char, ecc) non possono accettare un valore "NULL". Solo i puntatori possono avere un valore "NULL".
Tutti i tipi C # possono accettare null come  valore
• T, con il supporto null è “T?”, ad es. int con il supporto null è “int?”
Nessun supporto per “foreach”
Supporto di foreach per iterare su una collezione
Supporto per try e catch.
Nessun supporto per "finally”
Supporto per try-catch-finally

Nessuna notifica su operazioni di overflow.
Per esempio:
unsigned x = UINT_MAX;
x++;
// x == 0 nessuna exception lanciata
Usando le parole chiave checked/unchecked puoi ottenere comportamenti differenti (exception/overflow)
Nessun supporto nativo per sezioni critiche o multithreading
Parola chiave lock per le critical sections, oggetti Thread built-in
All’interno di uno switch, il break è opzionale
Il break dentro lo switch è obbligatorio
Nessuna documentazione built-in. Esite la possibilità di usare tools esterni come doxygen
Documentazione XML con “///”. Riconosciuto da IntelliSense in Visual Studio
Non si può suddividere la definizione di una classe su più file
Le classi parziali permettono di suddividere la definizione di una classe su più source files.

Nessun supporto per mix di librerie di versioni differenti
Si può fare un mix di librerie di versioni differenti

Lo sviluppatore deve gestire la memoria direttamente (new/delete)

Supporto di Garbage collection
Si ha maggiore controllo sulla distruzione degli oggetti, possibilità di memory leaks se non ben gestita
Minore controllo sulla distruzione degli oggetti, ma più sicurezza a causa della gestione automatica del GC
Gli oggetti “reference” possono essere allocati sia nell’heap che nello stack della memoria dichiarandoli come locali o con una new
Gli oggetti “reference” vengono sempre allocati nell’heap
Supporto per classi o metodi “friend”
Non esiste il concetto di friend, ma questo è stato in qualche modo sostituito dalle Proprietà di una classe
I tipi non condividono un tipo root comune

Tutti i tipi derivano dalla classe “Object”


Pensandoci bene e leggendo vari articoli sull’argomento, ci si rende conto che in realtà la differenza tra un linguaggio come C++ e uno come C# (o Java), è ben più profonda e parte dalla base stessa di come è stato creato il linguaggio.
Il C++ si potrebbe definire come un linguaggio a due livelli di astrazione:

  • Livello 1 (low level): gestione diretta della memoria e dell’allocazione delle risorse attraverso i puntatori . A questo livello viene definita la classe come detentrice delle risorse che verranno rilasciate nel suo distruttore. L’acquisizione-inizializzazione-distruzione delle risorse usate dalla classe è completamente a carico del programmatore che deve essere quindi sufficientemente esperto per progetti di medie-grosse dimensioni.
  • Livello 2 (high level): a questo livello si vedono solo gli “oggetti” e si possono usare le risorse solo attraverso i metodi esposti dagli “oggetti”. Non esistono puntatori e nemmeno “delete” o “free”: al limite esistono puntatori “intelligenti” detti smart_pointer che si auto gestiscono e distruggono.  Si richiede un grado di esperienza del programmatore anche inferiore perché la gestione “delicata” delle risorse è fatta al livello sottostante. Questo livello è molto simile a quello offerto da C#/Java.

Le origini del C++ non partono esclusivamente dal C, di cui comunque si è scelto di mantenere la compatibilità, ma soprattutto da un’idea di Stroustrup che il linguaggio stesso potesse essere esteso, partendo da un nucleo decisamente ridotto all'osso. Il programmatore può decidere di costruire sul linguaggio nativo estensioni difficilmente distinguibili dal linguaggio stesso. Un chiaro esempio di ciò è la libreria STL.
Il C#, come il Java, lasciano meno possibilità e libertà al programmatore, che probabilmente però ne guadagna in “sicurezza”e velocità di apprendimento. C# come Java partono da un livello di astrazione superiore al C++ e però se da un lato ciò comporta l’evitare tutte quelle problematiche che possono insorgere quando si programma a basso livello, dall’altro ne perde in flessibilità e potenza espressiva. C’è da dire che probabilmente la scelta di Java/C# è oculata perchè il target dei linguaggi è molto differente: nel caso di Java/C# si hanno linguaggi destinati ad applicazioni che devono interagire pesantemente col mondo della rete, del web, dei database, delle applicazioni distribuite ecc., per cui si è scelto di semplificare la vita del programmatore dando un livello di astrazione maggiore: in questo modo è stato possibile costruire e far apprendere in tempi relativamente rapidi ambienti quali .NET o Java (inteso come ambiente). Il C++ è stato pensato invece per andare a coprire target quali applicazioni time critical o sistemi embedded: target per cui è conveniente poter agire a differenti livelli di astrazione. Dire che C# è un C++ "migliorato" non è corretto, è più corretto dire che C# è stato pensato per scopi differenti, usare l'uno o l'altro dipende da ciò che dobbiamo costruire.

3 commenti:

Unknown ha detto...

Alla lista sopra aggiungo inoltre che C++ supporta l'ereditarietà multipla delle classi, mentre c# no.

Alberto ha detto...

Il C# supporta gli indicizzatori, mentre il C++ non prevede gli indicizzatori, ma lo stesso effetto può essere ottenuto con l'overloading dell'operatore parentesi quadra.

Anonimo ha detto...

Bravo, ottimo articolo, d'accordo in tutto; se vai sul web trovi tutti commenti che sottovalutano in una maniera irrazionale C# e in casi rari C++; invece sono entrambi potenza pura che può essere efficace o non a seconda dell'applicazione che si sviluppa

Posta un commento

 
Design by Free WordPress Themes | Bloggerized by Lasantha - Premium Blogger Themes | cna certification