Esercizio C++ : Trovare errori nell'implementazione

Gi81
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.
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
Gi81
up

hamming_burst
Ciao,
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=1
come 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ì :-)

Gi81
Ciao hamming_burst, grazie per la risposta :-)
"hamming_burst":
no qua la cosa da modificare/aggiungere è:
 ~Contenitore(){
      delete[] ps;
   }   

essendo ps un array.
Ci avevo pensato anch'io,
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?

apatriarca
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:
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.

Gi81
Grazie apatriarca. Tutto chiaro.
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?

apatriarca
L'unico ordine che importanza è quello con cui le variabili membro sono dichiarate nella classe:
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.

Gi81
Grazie mille!

hamming_burst
"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++...

Rispondi
Per rispondere a questa discussione devi prima effettuare il login.