Secondo me, i pericoli del C ++ sono alquanto esagerati.
Il pericolo essenziale è questo: mentre C # ti permette di eseguire operazioni di puntatore "non sicure" usando la parola chiave unsafe
, C ++ (essendo principalmente un superset di C) ti permetterà di usare i puntatori ogni volta che ne hai voglia. Oltre ai soliti pericoli inerenti all'uso dei puntatori (che sono gli stessi di C), come perdite di memoria, overflow del buffer, puntatori penzolanti, ecc., Il C ++ introduce nuovi modi per rovinare seriamente le cose.
Questa "corda extra", per così dire, che Joel Spolsky parlava , fondamentalmente si riduce a una cosa: scrivere classi che gestiscono internamente la propria memoria, noto anche come " Regola di 3 "(che ora può essere chiamato Regola di 4 o Regola di 5 in C ++ 11). Questo significa che se vuoi scrivere una classe che gestisce internamente le proprie allocazioni di memoria, devi sapere cosa stai facendo, altrimenti il tuo programma andrà in crash. Devi creare con cura un costruttore, un costruttore di copia, un distruttore e un operatore di assegnazione, il che è sorprendentemente facile da sbagliare, spesso causa di bizzarri arresti anomali in fase di runtime.
TUTTAVIA , nella programmazione C ++ attuale ogni giorno, è molto raro infatti scrivere una classe che gestisce la propria memoria, quindi è fuorviante dire che i programmatori C ++ sempre Bisogna essere "attenti" per evitare queste insidie. Di solito, farai qualcosa di più simile a:
class Foo
{
public:
Foo(const std::string& s)
: m_first_name(s)
{ }
private:
std::string m_first_name;
};
Questa classe sembra molto vicina a ciò che faresti in Java o C # - non richiede una gestione esplicita della memoria (perché la classe della libreria std::string
si occupa di tutto ciò automaticamente), e nessuna "regola di 3" è roba richiesto a tutti dal momento che il costruttore di copie e l'operatore di assegnazione predefiniti vanno bene.
È solo quando provi a fare qualcosa del tipo:
class Foo
{
public:
Foo(const char* s)
{
std::size_t len = std::strlen(s);
m_name = new char[len + 1];
std::strcpy(m_name, s);
}
Foo(const Foo& f); // must implement proper copy constructor
Foo& operator = (const Foo& f); // must implement proper assignment operator
~Foo(); // must free resource in destructor
private:
char* m_name;
};
In questo caso, può essere difficile per i principianti ottenere il compito, il distruttore e il costruttore di copie corretti. Ma per la maggior parte dei casi, non c'è motivo di farlo mai. C ++ rende molto facile evitare la gestione manuale della memoria il 99% delle volte utilizzando classi di libreria come std::string
e std::vector
.
Un altro problema correlato è la gestione manuale della memoria in un modo che non tiene conto della possibilità che venga lanciata un'eccezione. Come:
char* s = new char[100];
some_function_which_may_throw();
/* ... */
delete[] s;
Se some_function_which_may_throw()
effettivamente fa genera un'eccezione, ti rimane una perdita di memoria perché la memoria allocata per s
non sarà mai recuperata. Ma ancora una volta, in pratica, questo non è più un problema per la stessa ragione per cui la "Regola di 3" non è più un gran problema. È molto raro (e di solito non necessario) gestire effettivamente la tua memoria con puntatori grezzi. Per evitare il problema precedente, tutto ciò che devi fare è usare std::string
o std::vector
, e il distruttore verrà automaticamente richiamato durante lo srotolamento dello stack dopo che è stata lanciata l'eccezione.
Quindi, un tema generale qui è che molte delle caratteristiche del C ++ che erano non ereditate da C, come l'inizializzazione / distruzione automatica, i costruttori di copia e le eccezioni, costringono un programmatore a prestare la massima attenzione quando fa gestione manuale della memoria in C ++. Ma ancora una volta, questo è solo un problema se si intende fare prima la gestione manuale della memoria, che non è quasi mai più necessaria quando si dispone di contenitori standard e puntatori intelligenti.
Quindi, secondo me, mentre C ++ ti dà un sacco di corda extra, non è quasi mai necessario usarlo per appendere te stesso, e le insidie di cui parlava Joel sono banalmente facili da evitare nel moderno C ++.