Esercizio C++ : Trovare errori nell'implementazione
L'implementazione della classe Contenitore è errata. Quali sono i problemi? Modificare la classe per eliminarli.
Discutere se la soluzione proposta è corretta anche in presenza di eccezioni.
Direi che va modficato il costruttore di copia, e va messo così:
E anche il distruttore necessita una modifica:
Vi chiedo: va bene così?
Secondo me nel distruttore non va aggiunto il delete: dato che non sono stati creati oggetti puntati, è inutile cancellare tali oggetti. No?
C'è altro da aggiungere?
Discutere se la soluzione proposta è corretta anche in presenza di eccezioni.
template <typename T> class Contenitore { public: Contenitore(int size) : sz(size), ps(new T[sz]){ } ~Contenitore(){ delete ps; } private: T* ps; unsigned int sz; };
Direi che va modficato il costruttore di copia, e va messo così:
Contenitore(unsigned int size=0) : sz(size), ps(new T[sz]){ }
E anche il distruttore necessita una modifica:
~Contenitore(){ ps=0; sz=0; }
Vi chiedo: va bene così?
Secondo me nel distruttore non va aggiunto il delete: dato che non sono stati creati oggetti puntati, è inutile cancellare tali oggetti. No?
C'è altro da aggiungere?
Risposte
up
Ciao,
premetto che le mie conoscenze del C++ sono derivate da Java e C. Detto questo, vediamo...
io direi che puoi mettere
no qua la cosa da modificare/aggiungere è:
essendo ps un array.
dovrebbe essere ok così
premetto che le mie conoscenze del C++ sono derivate da Java e C. Detto questo, vediamo...
Direi che va modficato il costruttore di copia, e va messo così:
Contenitore(unsigned int size=0) : sz(size), ps(new T[sz]){ }
io direi che puoi mettere
unsigned int size=1come valore di default, $0$ non avrebbe molta utilità...
E anche il distruttore necessita una modifica:
~Contenitore(){ ps=0; sz=0; }
no qua la cosa da modificare/aggiungere è:
~Contenitore(){ delete[] ps; }
essendo ps un array.
dovrebbe essere ok così

Ciao hamming_burst, grazie per la risposta
poi ho riflettuto su questo: il delete[] cancella tutti valori dell'array, però ps ora punta a qualcosa di non ben definito.
Non conviene imporre ps=0, giusto per fare le cose fatte bene?

"hamming_burst":Ci avevo pensato anch'io,
no qua la cosa da modificare/aggiungere è:
~Contenitore(){ delete[] ps; }
essendo ps un array.
poi ho riflettuto su questo: il delete[] cancella tutti valori dell'array, però ps ora punta a qualcosa di non ben definito.
Non conviene imporre ps=0, giusto per fare le cose fatte bene?
Mi dispiace, ma non ci siamo ancora con la correzione del codice. Un primo problema riguarda l'ordine con il quale vengono inizializzate le variabili membro in una initialization list. Lo standard richiede che l'ordine segua l'ordine con il quale le variabili membro sono elencate nella classe e non nella initialization list. In questo caso, viene quindi prima inizializzato ps e poi sz. ps viene quindi creato a partire da sz che non è ancora stato inizializzato. In questi casi, preferisco non includere in una initialization list variabili membro che dipendono da altre variabili membro. Per cui preferisco scrivere:
oppure eliminare la dipendenza tra le due variabili
In alternativa si può risolvere anche il problema riordinando le variabili membro in modo che sz sia dichiarata prima di ps. Ma è la soluzione che sconsiglio in quanto è difficile da applicare più in generale e può portare a bug difficili da trovare (anche se in effetti il compilatore è normalmente abbastanza efficace nel segnalare questo problema). Non mi è chiaro per quale motivo tu abbia deciso di fornire un valore di default a size, difficilmente è utile. Cambierei invece il tipo di size in modo che sia unsigned. In caso contrario, size potrebbe essere negativo e mettere la classe in uno stato non valido qualora non fosse gestito correttamente. Avrei quindi insomma scritto qualcosa come il seguente per il costruttore
L'altra correzione è invece quella già segnalata da hamming_burst di usare delete[] invece che delete nel distruttore.
Contenitore(int size) : sz(size) { ps = new T[sz]; }
oppure eliminare la dipendenza tra le due variabili
Contenitore(int size) : sz(size), ps(new T[size]){ }
In alternativa si può risolvere anche il problema riordinando le variabili membro in modo che sz sia dichiarata prima di ps. Ma è la soluzione che sconsiglio in quanto è difficile da applicare più in generale e può portare a bug difficili da trovare (anche se in effetti il compilatore è normalmente abbastanza efficace nel segnalare questo problema). Non mi è chiaro per quale motivo tu abbia deciso di fornire un valore di default a size, difficilmente è utile. Cambierei invece il tipo di size in modo che sia unsigned. In caso contrario, size potrebbe essere negativo e mettere la classe in uno stato non valido qualora non fosse gestito correttamente. Avrei quindi insomma scritto qualcosa come il seguente per il costruttore
Contenitore(unsigned size) : sz(size), ps(new T[size]){ }
L'altra correzione è invece quella già segnalata da hamming_burst di usare delete[] invece che delete nel distruttore.
Grazie apatriarca. Tutto chiaro.
Solo, non mi torna una cosa
Visto che deve essere inizializzato prima ps e dopo sz, bisogna scrivere così:
edit: credo di avere capito. Non importa l'ordine con cui l'ho scritto, importa il fatto che ps (che viene inizializzato prima di sz) non dipenda da sz. L'ordine con cui scrivo è ininfluente. Right?
Solo, non mi torna una cosa
"apatriarca":
...lo standard richiede che l'ordine segua l'ordine con il quale le variabili membro sono elencate nella classe e non nella initialization list...
Avrei quindi insomma scritto qualcosa come il seguente per il costruttore
Contenitore(unsigned size) : sz(size), ps(new T[size]){ }
Visto che deve essere inizializzato prima ps e dopo sz, bisogna scrivere così:
Contenitore(unsigned size) : ps(new T[size], sz(size)){ }o sbaglio?
edit: credo di avere capito. Non importa l'ordine con cui l'ho scritto, importa il fatto che ps (che viene inizializzato prima di sz) non dipenda da sz. L'ordine con cui scrivo è ininfluente. Right?
L'unico ordine che importanza è quello con cui le variabili membro sono dichiarate nella classe:
L'ordine nella initialization list non ha alcun effetto. Un altro modo per risolvere il problema era in effetti quello di scambiare l'ordine di dichiarazione delle variabili. Ma sinceramente credo che sia più saggio eliminare la dipendenza tra le due variabili nella initialization list. Sono dovuto andarla a controllare in effetti questa regola, per cui non ne farei troppo affidamento, potresti dimenticarla e poi fare casini.
private: T* ps; unsigned int sz;
L'ordine nella initialization list non ha alcun effetto. Un altro modo per risolvere il problema era in effetti quello di scambiare l'ordine di dichiarazione delle variabili. Ma sinceramente credo che sia più saggio eliminare la dipendenza tra le due variabili nella initialization list. Sono dovuto andarla a controllare in effetti questa regola, per cui non ne farei troppo affidamento, potresti dimenticarla e poi fare casini.
Grazie mille!
"apatriarca":
Un primo problema riguarda l'ordine con il quale vengono inizializzate le variabili membro in una initialization list. Lo standard richiede che l'ordine segua l'ordine con il quale le variabili membro sono elencate nella classe e non nella initialization list.
non pensavo che fosse così restrittivo lo standard del C++...