[C++] Date un'occhiata a questo codice?
Il codice viene compilato e funziona bene.
Ad ogni modo volevo conoscere il parere di qualcun'altro, per vedere se esistono soluzioni più eleganti.
Traccia esercizietto:
Soluzione(?):
Vi ringrazio.
EDIT: riguardando il codice mi sono accorto di aver fatto un errore che mi era già stato corretto quì sul forum. La variabile $N$ non è costante lungo tutta l'esecuzione del programma.
In realtà ci avevo ragionato un po' e quella adottata mi sembrava la soluzione più elegante, in vista dell'uso della funzione "pulisci". Ad ogni modo, di seguito: una modifica veloce al codice (ho compilato di nuovo e funziona ancora) per far rimanere $N$ costante, e usare una variabile che tenga il conto del numero di elementi caricati.
Sinceramente, mi sembrava più compatto il primo codice che ho postato.
Ad ogni modo volevo conoscere il parere di qualcun'altro, per vedere se esistono soluzioni più eleganti.
Traccia esercizietto:
Soluzione(?):
#include <iostream> #include <fstream> using namespace std; struct myStruct{ int numSettore; float totSettore; float percentage; int numAcquisti; }; void loadInfos(myStruct*, const float&, int&, int&); void stampaVettore(myStruct*, const int&, int&); void pulisci(myStruct*, int&); void copyStruct(myStruct*, int, int); int main(){ ifstream ingresso; int giorno, codice; float spesa; float totSpesa = 0; int N = 10; // Non e' costante. myStruct v[N]; //Inizializzo il vettore for(int j = 0; j < N; j++){ v[j].numSettore = j; v[j].totSettore = 0; v[j].numAcquisti = 0; } ingresso.open("dove.dat"); ingresso >> giorno >> codice; ingresso >> spesa; while(!ingresso.eof()){ for(int j = 0; j < N; j++) if(v[j].numSettore == codice){ v[j].numAcquisti++; v[j].totSettore += spesa; totSpesa += spesa; break; } ingresso >> giorno >> codice; ingresso >> spesa; } ingresso.close(); pulisci(v, N); //La pulizia avviene prima (!) di caricare le restanti informazioni //in modo da non dover leggere posti inutili dell'array. int minimo = 0; loadInfos(v, totSpesa, minimo, N); stampaVettore(v, minimo, N); return 0; } void stampaVettore(myStruct* v, const int& minimo, int& numElementi){ for(int j = 0; j < numElementi; j++){ cout << v[j].numSettore << " " << v[j].totSettore << " " <<v[j].percentage << " "; cout << v[j].numAcquisti << endl; } cout << "\nIl settore che ha gravato di meno sulla "; cout << "spesa totale e' il numero " << v[minimo].numSettore << ", con una spesa pari al "; cout << v[minimo].percentage << "% del totale\n"; return; } void loadInfos(myStruct* v, const float& totSpesa, int& minimo, int& numElementi){ for(int j = 0; j < numElementi; j++){ float c; c = v[j].totSettore / totSpesa; v[j].percentage = 100*c; //E' possibile riscrivere le tre istruzioni sopra //in un'unica riga, senza allocare spazio per c. //Ho scelto di sacrificare un po' //di memoria per avere più chiarezza //nel codice sorgente. if(v[j].percentage < v[minimo].percentage) minimo = j; } return; } void pulisci(myStruct* v, int& numElementi){ for(int j = 0; j < numElementi; j++){ if(v[j].numAcquisti == 0){ for(int k = j; k < numElementi - 1; k++) copyStruct(v, k, k+1); numElementi = numElementi - 1; } } return; } void copyStruct(myStruct* v, int firstIndex, int secondIndex){ v[firstIndex].numSettore = v[secondIndex].numSettore; v[firstIndex].totSettore = v[secondIndex].totSettore; v[firstIndex].numAcquisti = v[secondIndex].numAcquisti; return; }
Vi ringrazio.

EDIT: riguardando il codice mi sono accorto di aver fatto un errore che mi era già stato corretto quì sul forum. La variabile $N$ non è costante lungo tutta l'esecuzione del programma.
In realtà ci avevo ragionato un po' e quella adottata mi sembrava la soluzione più elegante, in vista dell'uso della funzione "pulisci". Ad ogni modo, di seguito: una modifica veloce al codice (ho compilato di nuovo e funziona ancora) per far rimanere $N$ costante, e usare una variabile che tenga il conto del numero di elementi caricati.
Sinceramente, mi sembrava più compatto il primo codice che ho postato.
Risposte
Io avrei un po' semplificato il codice osservando che tutte le informazioni richieste sono calcolabili a partire da due array: uno di double* di lunghezza 10 (che sembrerebbe costante dal testo) contenente il totale per ogni categoria merceologica e uno di interi, sempre di lunghezza 10, contenente il numero di acquisti per ogni categoria. Infatti
1. Il totale della spesa è semplicemente la somma degli elementi dell'array 1.
2. Cosa ha speso per ogni categoria merceologica è semplicemente il contenuto dell'array 1 e la percentuale è semplicemente il contenuto dell'array 1 diviso per il totale della spesa.
3. L'indice del valore massimo in array 2 è la categoria più acquistata nel mese.
4. L'indice del valore minimo dell'array 1 tale che il corrispondente valore dell'array 2 è maggiore di zero è la categoria richiesta nell'ultimo punto.
Venendo al codice:
Se il file fosse vuoto le prime letture fallirebbero. Ancora peggio, se il file avesse una sola riga e non ci fosse neanche uno spazio dopo spesa, ingresso.eof() sarebbe a quel punto vero e la prima riga non verrebbe presa in considerazione. Lo stesso discorso vale per la lettura alla fine. Inoltre
v[j].numSettore == j per costruzione dell'array. Non c'è alcun bisogno di scrivere un ciclo del genere (o anche solo di memorizzare il numero del settore nell'array).
Non mi è chiaro perché complicarsi la vita con la funzione pulisci. Era sufficiente aggiungere i test numAquisti > 0 dove necessario.
La traduzione tra codice sorgente e codice macchina è tutt'altro che letterale. E' possibile/probabile che non ci sia alcuna differenza tra le due versioni del codice.
Sbaglio o non hai stampato qual'è la categoria merceologica con il maggior numero di acquisti?
* La spesa è un double nel testo e non un float come nel tuo codice.
EDIT: riguardo all'N. Molti compilatori non compilerebbero il tuo primo codice perché non permettono la creazione di un array di dimensione non costante a compile-time. Per cui la seconda versione è l'unica accettata dallo standard del linguaggio. Per il resto i due codice mi sembrano identici.. Perché mai ritieni che il primo codice fosse più compatto?
1. Il totale della spesa è semplicemente la somma degli elementi dell'array 1.
2. Cosa ha speso per ogni categoria merceologica è semplicemente il contenuto dell'array 1 e la percentuale è semplicemente il contenuto dell'array 1 diviso per il totale della spesa.
3. L'indice del valore massimo in array 2 è la categoria più acquistata nel mese.
4. L'indice del valore minimo dell'array 1 tale che il corrispondente valore dell'array 2 è maggiore di zero è la categoria richiesta nell'ultimo punto.
Venendo al codice:
ingresso.open("dove.dat"); ingresso >> giorno >> codice; ingresso >> spesa; while(!ingresso.eof()){
Se il file fosse vuoto le prime letture fallirebbero. Ancora peggio, se il file avesse una sola riga e non ci fosse neanche uno spazio dopo spesa, ingresso.eof() sarebbe a quel punto vero e la prima riga non verrebbe presa in considerazione. Lo stesso discorso vale per la lettura alla fine. Inoltre
for(int j = 0; j < N; j++) if(v[j].numSettore == codice){ v[j].numAcquisti++; v[j].totSettore += spesa; totSpesa += spesa; break; }
v[j].numSettore == j per costruzione dell'array. Non c'è alcun bisogno di scrivere un ciclo del genere (o anche solo di memorizzare il numero del settore nell'array).
Non mi è chiaro perché complicarsi la vita con la funzione pulisci. Era sufficiente aggiungere i test numAquisti > 0 dove necessario.
//E' possibile riscrivere le tre istruzioni sopra
//in un'unica riga, senza allocare spazio per c.
//Ho scelto di sacrificare un po'
//di memoria per avere più chiarezza
//nel codice sorgente.
La traduzione tra codice sorgente e codice macchina è tutt'altro che letterale. E' possibile/probabile che non ci sia alcuna differenza tra le due versioni del codice.
Sbaglio o non hai stampato qual'è la categoria merceologica con il maggior numero di acquisti?
* La spesa è un double nel testo e non un float come nel tuo codice.
EDIT: riguardo all'N. Molti compilatori non compilerebbero il tuo primo codice perché non permettono la creazione di un array di dimensione non costante a compile-time. Per cui la seconda versione è l'unica accettata dallo standard del linguaggio. Per il resto i due codice mi sembrano identici.. Perché mai ritieni che il primo codice fosse più compatto?
"apatriarca":
Venendo al codice:
ingresso.open("dove.dat"); ingresso >> giorno >> codice; ingresso >> spesa; while(!ingresso.eof()){
Se il file fosse vuoto le prime letture fallirebbero. Ancora peggio, se il file avesse una sola riga e non ci fosse neanche uno spazio dopo spesa, ingresso.eof() sarebbe a quel punto vero e la prima riga non verrebbe presa in considerazione. Lo stesso discorso vale per la lettura alla fine.
Credi che scritto come segue andrebbe meglio?
do{ ifstream ingresso; ingresso.open("..."); ingresso >> //... }while(!ingresso.eof());
"apatriarca":
Perché mai ritieni che il primo codice fosse più compatto?
Perché per la stessa necessità, quella di avere una variabile aggiornata sul numero di posti occupati nell'array, ho usato una sola variabile.
Nel secondo codice invece dichiaro N all'inizio e tengo occupata memoria fisica fino alla fine del programma. Penso male?
***
Il codice l'ho riscritto, sfruttando i consigli di apatriarca. Rinnovo la richiesta del titolo.

E aggiungo: passare variabili per riferimento costanti è una buona abitudine quando l'uso che se ne deve fare è di sola lettura?
#include <iostream> #include <fstream> using namespace std; struct myStruct{ int numAcquisti; double totaleSpesoCategoria; float percentage; }; void inizializzaVettore(myStruct*, const int&); int secondaRichiesta (myStruct*, const double&, const int&); //const myStruct* (?) ... int terzaRichiesta (const myStruct*, const int&); void printResults (const myStruct*, const int&, const double&, const int&, const int&); int main(){ ifstream ingresso; ingresso.open("dove.dat"); const int N = 10; myStruct v[N]; inizializzaVettore(v, N); int giorno, numCategoria; double spesaAcquisto; double totaleSpeso = 0; do{ ingresso >> giorno >> numCategoria; ingresso >> spesaAcquisto; v[numCategoria].numAcquisti++; v[numCategoria].totaleSpesoCategoria += spesaAcquisto; totaleSpeso += spesaAcquisto; }while(!ingresso.eof()); ingresso.close(); int indexMinPercentage = secondaRichiesta (v, totaleSpeso, N); int indexMaxNumber = terzaRichiesta (v, N); printResults (v, N, totaleSpeso, indexMinPercentage, indexMaxNumber); cout << "\nPremi <return> per uscire" << endl; cin.get(); return 0; } void printResults(const myStruct* v, const int& N, const double& totaleSpeso, const int& indexMin, const int& indexMax){ cout << "\nLa spesa totale di questo mese e' stata " << totaleSpeso << " euro." << endl; for(int j = 0; j < N; j++){ if(v[j].numAcquisti > 0){ cout << "\nPer la categoria merceologica numero " << j << " "; cout << "il numero di prodotti acquistati e' " << v[j].numAcquisti << " "; cout << "per un totale di " << v[j].totaleSpesoCategoria << " "; cout << "euro cioe', " << v[j].percentage << "% del totale.\n"; } } cout << "\nLa categoria merceologica con il piu' alto numero di acquisti e' stata la "; cout << "numero " << indexMax << ", per un totale di "; cout << v[indexMax].numAcquisti << " acquisti.\n"; cout << "\nLa categoria merceologica che ha gravato di meso sulla spesa del mese "; cout << "e' stata la numero " << indexMin << ", per un totale di "; cout << v[indexMin].percentage << "% del totale speso.\n"; return; } void inizializzaVettore(myStruct* v, const int& N){ for(int j = 0; j < N; j++){ v[j].numAcquisti = 0; v[j].totaleSpesoCategoria = 0; } return; } int secondaRichiesta (myStruct* v, const double& totaleSpeso, const int& N){ int indexMinimo = 0; for(int j = 0; j < N; j++){ if(v[j].numAcquisti > 0){ v[j].percentage = 100 * (v[j].totaleSpesoCategoria / totaleSpeso); if(v[j].percentage < v[indexMinimo].percentage) indexMinimo = j; } } return indexMinimo; } int terzaRichiesta (const myStruct* v, const int& N){ int maxIndex = v[0].numAcquisti; for(int j = 0; j < N; j++){ if(v[j].numAcquisti > v[maxIndex].numAcquisti) maxIndex = j; } return maxIndex; }
Secondo me dovresti evitare di fare supposizioni su come il codice verrà convertito in codice macchina e sull'effettivo utilizzo di memoria. Una costante come quella non occupa alcuna memoria, ma viene semplicemente usato il valore 10 ogni volta che utilizzi quella costante. Al contrario, non è detto che il compilatore sia in grado di fare la stessa cosa quando N non sia dichiarato come costante (l'unica cosa da fare per saperlo è leggersi il codice assembly prodotto).
Il passaggio per riferimento non è poi sempre meglio del passaggio per valore. Per prima cosa, ad ogni utilizzo di un reference, è necessario deferenziare un indirizzo (ma è possibile che il compilatore sia in grado di ottimizzare questa cosa). Inoltre, un puntatore/reference non è la variabile più piccola che ci sia. In particolare, nelle architetture a 32bit occupa solitamente 4 byte e in quelle a 64bit 8 byte. Un intero occupa solitamente la stessa quantità di memoria o meno. Non sono al corrente di architetture in cui un int sia più grosso di un puntatore. Per questa ragione non ha senso passare un intero come reference a meno di volerlo modificare all'interno della funzione.
Per finire il discorso di performance. Il tuo programma occupa una quantità di memoria che probabilmente è più piccolo della cache L1 del tuo processore. Non arrivi neanche ad un KB.. Sul tuo sistema ci saranno almeno 2-4GB di memoria RAM. Fidati che non ha alcun senso preoccuparsi dell'occupazione di memoria in programmi di questo tipo.
Ma perché utilizzi un do while? Devi verificare subito se il file è vuoto.. Per cui usa un while con la stessa condizione.
Il passaggio per riferimento non è poi sempre meglio del passaggio per valore. Per prima cosa, ad ogni utilizzo di un reference, è necessario deferenziare un indirizzo (ma è possibile che il compilatore sia in grado di ottimizzare questa cosa). Inoltre, un puntatore/reference non è la variabile più piccola che ci sia. In particolare, nelle architetture a 32bit occupa solitamente 4 byte e in quelle a 64bit 8 byte. Un intero occupa solitamente la stessa quantità di memoria o meno. Non sono al corrente di architetture in cui un int sia più grosso di un puntatore. Per questa ragione non ha senso passare un intero come reference a meno di volerlo modificare all'interno della funzione.
Per finire il discorso di performance. Il tuo programma occupa una quantità di memoria che probabilmente è più piccolo della cache L1 del tuo processore. Non arrivi neanche ad un KB.. Sul tuo sistema ci saranno almeno 2-4GB di memoria RAM. Fidati che non ha alcun senso preoccuparsi dell'occupazione di memoria in programmi di questo tipo.
Ma perché utilizzi un do while? Devi verificare subito se il file è vuoto.. Per cui usa un while con la stessa condizione.
"apatriarca":
Secondo me dovresti evitare di fare supposizioni su come il codice verrà convertito in codice macchina e sull'effettivo utilizzo di memoria. Una costante come quella non occupa alcuna memoria, ma viene semplicemente usato il valore 10 ogni volta che utilizzi quella costante. Al contrario, non è detto che il compilatore sia in grado di fare la stessa cosa quando N non sia dichiarato come costante (l'unica cosa da fare per saperlo è leggersi il codice assembly prodotto).
Il passaggio per riferimento non è poi sempre meglio del passaggio per valore. Per prima cosa, ad ogni utilizzo di un reference, è necessario deferenziare un indirizzo (ma è possibile che il compilatore sia in grado di ottimizzare questa cosa). Inoltre, un puntatore/reference non è la variabile più piccola che ci sia. In particolare, nelle architetture a 32bit occupa solitamente 4 byte e in quelle a 64bit 8 byte. Un intero occupa solitamente la stessa quantità di memoria o meno. Non sono al corrente di architetture in cui un int sia più grosso di un puntatore. Per questa ragione non ha senso passare un intero come reference a meno di volerlo modificare all'interno della funzione.
Purtroppo sono aspetti che non ho avuto modo di approfondire. Piuttosto che usare memoria a sbafo ho preferito "immaginare" come sarebbe stata usata la memoria -certo sarebbe stato meglio sapere.
"apatriarca":
Per finire il discorso di performance. Il tuo programma occupa una quantità di memoria che probabilmente è più piccolo della cache L1 del tuo processore. Non arrivi neanche ad un KB.. Sul tuo sistema ci saranno almeno 2-4GB di memoria RAM. Fidati che non ha alcun senso preoccuparsi dell'occupazione di memoria in programmi di questo tipo.
Certo, ma non sarebbe bene tendere comunque ad occupare meno memoria possibile a prescindere dalle capacità della macchina?
"apatriarca":
Ma perché utilizzi un do while? Devi verificare subito se il file è vuoto.. Per cui usa un while con la stessa condizione.
Ci provo.
Innanzi tutto un dubbio: ho provato a leggere da un file di una sola riga senza nemmeno uno spazio alla fine e non sono riuscito a verificare quanto mi hai detto, nel senso che il programma gira. Invece se il file è completamente vuoto dal terminale ricevo un segmentation fault.
Ora, se il file è vuoto potrei uscire dal programma. Se è vero, come dicevi nel messaggio precedente, che "ingresso.eof()" resituisce $1$ anche dopo aver letto solo la prima riga -se non vi è uno spazio-, credo che allora la prima lettura e il primo caricamento della riga vadando fuori dal $"while"$.
Cioé:
//... ifstream ingresso; ingresso.open(*/.../*); ingresso >> giorno >> numCategoria; ingresso >> spesaAcquisto; if(ingresso.eof()){ if(0 <= numCategoria && numCategoria <= 9){ v[numCategoria].numAcquisti++; v[numCategoria].totaleSpesoCategoria += spesaAcquisto; totaleSpeso += spesaAcquisto; ingresso >> giorno >> numCategoria; ingresso >> spesaAcquisto; } else { cout << "Error" << endl; return 1; } while(!ingresso.eof()){ //Caricamento.. //... ingresso >> giorno >> numCategoria; ingresso >> spesaAcquisto; }
Certo: così la variabile $"numCategoria"$ che non è inizializzata in partenza potrebbe, per un caso sfortunato, assumere un valore fra $0$ e $9$, anche se il file fosse completamente vuoto. (Giusto? Se la variabile non è inizializzata potrebbe assumere qualsiasi valore accettabile per il suo tipo).
Ok, ho sbagliato di nuovo. Qualche suggerimento?
Ringrazio apatriarca per il tempo e per la pazienza che mi sta dedicando!
La struttura generale per una lettura riga per riga di un file è la seguente:
Volendolo scrivere in C++ è quindi qualcosa come il seguente:
Dipende da quali sono i tuoi obiettivi. A volte è necessario usare qualche byte in più. In ogni caso nel tuo programma il maggiore "spreco di memoria" si ha nell'uso di tre variabili membro nella struttura invece che due come ti avevo scritto. La percentuale può infatti essere calcolata sul momento a partire dalla spesa totale e dalla spesa cumulativa del settore merceologico. Sono infatti 4 * 10 = 40 byte.. Le altre variabili che hai considerato erano invece solo di 4-8 byte. In ogni caso, alcuni compilatori sono in grado di fare trucchetti strani con le variabili passate alle funzioni. Le funzioni potrebbero infatti del tutto sparire, oppure alcuni argomenti potrebbero venire passati direttamente nei registri del processore.
apertura file finché il file non è terminato leggi una riga e memorizza le informazioni che cerchi chiudere il file
Volendolo scrivere in C++ è quindi qualcosa come il seguente:
ifstream in(filename); while ( in ) { // leggi una riga di input } // non è necessario chiudere il file perché lo fa già il distruttore di in
Certo, ma non sarebbe bene tendere comunque ad occupare meno memoria possibile a prescindere dalle capacità della macchina?
Dipende da quali sono i tuoi obiettivi. A volte è necessario usare qualche byte in più. In ogni caso nel tuo programma il maggiore "spreco di memoria" si ha nell'uso di tre variabili membro nella struttura invece che due come ti avevo scritto. La percentuale può infatti essere calcolata sul momento a partire dalla spesa totale e dalla spesa cumulativa del settore merceologico. Sono infatti 4 * 10 = 40 byte.. Le altre variabili che hai considerato erano invece solo di 4-8 byte. In ogni caso, alcuni compilatori sono in grado di fare trucchetti strani con le variabili passate alle funzioni. Le funzioni potrebbero infatti del tutto sparire, oppure alcuni argomenti potrebbero venire passati direttamente nei registri del processore.
"apatriarca":
In ogni caso nel tuo programma il maggiore "spreco di memoria" si ha nell'uso di tre variabili membro nella struttura invece che due come ti avevo scritto.
Sì, hai ragione. Togliere il membro $"percentage"$ e stampare direttamente le percentuali poco prima di averle calcolate non dovrei trovarlo complicato. Più tardi provo a postare come ho risolto.
"apatriarca":
La struttura generale per una lettura riga per riga di un file è la seguente:
apertura file finché il file non è terminato leggi una riga e memorizza le informazioni che cerchi chiudere il file
Volendolo scrivere in C++ è quindi qualcosa come il seguente:
ifstream in(filename); while ( in ) { // leggi una riga di input } // non è necessario chiudere il file perché lo fa già il distruttore di in
Ok. Ma come posso sbrigarmela con il problema che il file potrebbe essere vuoto, o potrebbe avere una sola riga senza spazio al termine?
Il brutto espediente del post precedente a questo come lo vedi?
Il codice che ti ho postato dovrebbe gestire senza alcun problema i due casi da te menzionati.
"giuscri":
Certo, ma non sarebbe bene tendere comunque ad occupare meno memoria possibile a prescindere dalle capacità della macchina?
No. È la famosa massima di Donald Knuth:
"Donald Knuth":
We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil
L'obiettivo principale è scrivere codice:
- corretto
- comprensibile (agli umani, non ai compilatori)
Solo una volta che questi due punti sono stati soddisfatti si vede se il programma ha problemi di efficienza (es. è troppo lento o occupa troppa memoria). Se (e solo se) ci sono problemi, si ottimizza il collo di bottiglia (e solo quello).
In alcuni casi si può essere tentati di scegliere strutture dati ottimizzate già durante la fase di progettazione, perché i futuri problemi di efficienza sono evidenti. Una dimostrazione di come questo approccio causi più danni che altro l'abbiamo direttamente nella libreria standard del C++: std::vector
Nota: non è una regola esente da eccezioni, ma è pur sempre una regola.
"giuscri":
Certo: così la variabile numCategoria che non è inizializzata in partenza potrebbe, per un caso sfortunato, assumere un valore fra 0 e 9, anche se il file fosse completamente vuoto. (Giusto? Se la variabile non è inizializzata potrebbe assumere qualsiasi valore accettabile per il suo tipo).
No. Se accedi ad una variabile non inizializzata il comportamento è indefinito. In alcuni ambienti può accadere che venga assegnato a quella variabile un valore casuale, ma in principio può succedere qualsiasi cosa.
Per controllare se l'estrazione di un valore da uno stream è andata a buon fine o meno devi controllare il failbit:
int k; std::cin >> k; if (std::cin.fail()) { // L'estrazione è fallita. Il valore di "k" è rimasto immutato*, quindi // non puoi accedere a "k", avresti comportamento indefinito. // * In C++11 a "k" viene assegnato zero. } else { // L'estrazione è riuscita. A "k" è stato assegnato il valore inserito // dall'utente. }
"apatriarca":
// non è necessario chiudere il file perché lo fa già il distruttore di in
Non sono molto d'accordo. Se non ci sono altre istruzioni prima della chiamata del distruttore potrebbe anche andare bene:
void read(std::string filename) { std::ifstream in(filename.c_str()); while (in) { // Leggi input. } } // Ok, qua appena finito di leggere il file la variabile "in" esce dallo // scope e viene chiamato il suo distruttore, che a sua volta chiama // "close()".
In questo caso invece si dovrebbe chiudere il file esplicitamente per rilasciare le risorse non appena non servono più (in questo esempio probabilmente non è importante, ma per un programma reale potrebbero esserci altre applicazioni che attendono di aprire quel file):
void read(std::string filename) { std::ifstream in(filename.c_str()); while (in) { // Leggi input. } // Qui abbiamo finito di usare il file, ma non rilasciamo la sua risorsa // per un po'. ... } // Solo qui il file viene chiuso. Nel frattempo la risorsa è stata // inutilmente impegnata.
"claudio86":
[quote="apatriarca"]// non è necessario chiudere il file perché lo fa già il distruttore di in
Non sono molto d'accordo. Se non ci sono altre istruzioni prima della chiamata del distruttore potrebbe anche andare bene:
[ ... CODICE ... ]
In questo caso invece si dovrebbe chiudere il file esplicitamente per rilasciare le risorse non appena non servono più (in questo esempio probabilmente non è importante, ma per un programma reale potrebbero esserci altre applicazioni che attendono di aprire quel file):
[ ... CODICE ... ]
[/quote]
Sì, certo. Non intendevo dire che era una buona idea quella di tenere il file aperto per lungo tempo anche dopo aver finito di usarlo. Ritengo tuttavia che usare il costruttore per chiudere il file è in generale un'idea migliore rispetto al chiuderlo esplicitamente (esattamente come penso sia meglio usare il costruttore per aprirlo). La ragione principale è che la risorsa aperta e la variabile che la rappresenta nel codice hanno la stessa "vita". In ogni caso è un pattern abbastanza comune in C++ da avere un nome (RAII - ma credo che tu lo conosca abbastanza bene). Nel tuo secondo esempio avrei racchiuso il file in un blocco, ma forse l'uso di una funzione come nel tuo primo esempio è preferibile. In un linguaggio come C# avrei utilizzato probabilmente un blocco using per lo stesso scopo.