Newsgroup: it.comp.lang.c++
Titolo: riferimenti

Rieccomi,
scusate se vi stresso con queste domande banali ma ci sono alcuni 
comportamenti del C++ che non riesco a capire
ho la seguente funzione statica:
Oggetto** Oggetto::inizializza_oggetti() {
	Oggetto ret** = new Oggetto*[5];
	for(int i=0; i<5; i++) ret[i] = new Oggetto(i);
	return ret;
}
nel main faccio
Oggetto** miei_oggetti = Oggetto::inizializza_oggetti();
miei_oggetti[0-4]->chiama_funzione();
e tutto va bene....siccome però sto imparando un linguaggio nuovo vorrei 
cercare di usare tutte le sue caratteristiche, e quindi i riferimenti; 
il codice di sopra dovrebbe diventare:
Oggetto*& Oggetto::inizializza_oggetti() {
	Oggetto ret** = new Oggetto*[5];
	for(int i=0; i<5; i++) ret[i] = new Oggetto(i);
	return ret[0];
}
cosa sto facendo? Se ho capito bene sto passando un puntatore per 
riferimento, quindi sto passando un tipo Oggetto**
A questo punto nel main vorrei fare esattamente come sopra, ma non 
funziona perchè il compilatore mi dice che la mia funzione ritorna un 
Oggetto* e non un Oggetto**, ma perchè?!
Se invece nel main faccio
Oggetto*& miei_oggetti = Oggetto::inizializza_oggetti();
la compilazione va a buon fine, ma se invoco le funzioni sugli oggetti 
dell'array, usando la notazione
	miei_oggetti[i].mia_funzione()
questa viene invocata ma non dà i risultati giusti quando i non è 0 cioè 
il primo oggetto dell'array...perchè?
Un'ultima domanda e poi cercherò di non annoiarvi più con queste 
banalità per un pò:
come dovrei modificare la funzione per fare in modo che i puntatori agli 
oggetti contenuti nell'array siano costanti? Cioè vorrei che una volta 
che la funzione mi ritorna l'array di puntatori agli oggetti non fosse 
possibile far puntare un qualsiasi indice dell'array ad un altro oggetto.
A titolo informativo il libro che sto leggendo è Thinking in C++ 2nd 
edition...forse non so più l'inglese?!?! :)
Grazie mille, Alberto

 qualsiasi indice dell'array ad un altro oggetto.
Cioe, vuol che il array sia costante. std::vector< Oggetto* >
const, insomma.
--
James Kanze                                           GABI Software
Conseils en informatique orientée objet/
                   Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, 
+33 (0)1 30 23 00 34

 non ho mai fatto un new di un array.
Ok, supponiamo di essere in uno di quei casi particolari; new di un 
array non devo farlo, quindi come dovrei fare? Come mi hai scritto te 
nella funzione?
Saluti e grazie, Alberto

     Oggetto ret** = new Oggetto*[5];
Non credo proprio.
Kiuhnm

Kiuhnm ha scritto:

Cosa non credi proprio scusa?
Eccola...
net_if* const* net_if::initialize_if() {
	struct if_nameindex* tmp = if_nameindex();
	net_if** ifaces = new net_if*[net_if::if_num];
	for(int i = 0; i < net_if::if_num; i++) {
		ifaces[i] = new net_if(tmp->if_name);
		tmp++;
	}
	if_freenameindex(tmp - net_if::if_num);
	return ifaces;
}
Ciao, Alberto

 Eccola...
Appunto. Non è la stessa.
La versione che hai dato è errata.
Guarda dove hai messo gli asterischi :)
Kiuhnm

Kiuhnm ha scritto:

Azzo, hai ragione :)
Grazie, Alberto

 il primo oggetto dell'array...perchè?
Ci credo. Perché ti vai a cimentare con cose così difficili già all'inizio?
La forma corretta è
(&o)[1]->mia_funzione() << endl;
Hai un array di 5 puntatori. Quello che tu hai fatto è stato applicare 
l'indice al primo puntatore contenuto nell'array, anziché all'array. 
Difficile che funzioni...
Kiuhnm

 
Ahhhhh, forse ho capito!! Un riferimento viene SEMPRE dereferenziato dal 
compilatore?!?! Maledetto thinking in C++ sta cosa non è spiegata per un 
cavolo proprio...
Un'ultima cosina, non ti chiedo di pensare ad una soluzione per me, ma 
solo una risposta secca SI/NO: funzione (la stessa di tutti i post) 
statica che costruisce (alloca memoria e chiama il costruttore per 
ognuno) un array di lunghezza variabile di puntatori oggetti e 
restituisce tale array (i puntatori devono essere costanti). Se tu, che 
dai tuoi post mi pare di capire abbia una buona conoscenza di C++, lo 
trovassi fatto nel codice di qualcun altro in questo modo:
Oggetto* const* Oggetto::inizializza() {
	Oggetto** ret = new Oggetto*[valore_deciso_a_run_time];
	for(bla bla bla...) ret[i] = new Oggetto(bla bla bla...);
	return ret;
}
la notte avresti gli incubi per un mese dallo schifo appena letto o ti 
parrebbe una soluzione accettabile?
Sii pure spietato :)
Ciao e grazie ancora, Alberto

 }
Perché usare dei puntatori se tanto saranno costanti?
Hai un qualche motivo per non allocare direttamente un array di oggetti?
Kiuhnm

Kiuhnm ha scritto:

Aiuto....allora, ti rispondo "si" ma ti espongo anche il perchè così 
magari mi dici, quando hai 5 min liberi, se dico bene.
Se usassi direttamente un array di oggetti e non di puntatori ad oggetti 
  dovrei fare una cosa del tipo
Oggetti* obj = new Oggetti[valore_deciso_runtime];
ma qui il compilatore vuole chiamare il costruttore per ogni oggetto 
dell'array che sto costruendo; i problemi sono ora 2:
1. Dovrei fornire un costruttore di default che non fa niente che mi 
serva per far funzionare quell'istruzione perchè il mio costruttore 
invece prende una stringa
2. Dovrei poi ciclare sull'array e chiamare su ogni oggetto una funzione 
che devo aggiungere (ad esempio inizializza) per sostituire il 
comportamento che avevo previsto per il costruttore con parametro
Dico bene?
Ciao, Alberto

 Dico bene?
Sì. Comunque una soluzione consiste nell'usare la placement new. Prima 
allochi un buffer sufficientemente grande e poi costruisci gli oggetti:
   char* raw_buf = new char[sizeof(Oggetto)*num];  // se sizeof(char)=1
   Oggetto* objs = (Oggetto*)raw_buf;
   for (int i = 0; i < num; ++i)
       new (objs+i) Oggetto(i);
O qualcosa del genere.
Gli oggetti vanno distrutti chiamando il distruttore! Usa la delete[] 
solo su raw_buf (non su where!).
   for (int i = 0; i < num; ++i)
       objs[i].~Oggetto();
   delete[] raw_buf;
Inoltre tieni presente che la new restituisce un indirizzo allineato in 
modo tale da poter contenere qualsiasi oggetto. Non ti sognare di usare 
un buffer allocato sullo stack.
Ti conviene informarti.
Kiuhnm

   char* raw_buf = new char[sizeof(Oggetto)*num];  // se sizeof(char)=1
è garantito che il cast (implicito) da char * a oggetto * sia 
sempre funzionante e l'allineamento corretto ?
In caso contrario, volendo allocare il buffer
Oggetto * obj_buf = new Oggetto [num] ();
si può invocare il costruttore di default con le () vuote ?
Vero che è meno efficiente, nel senso che alla fine ci si riduce 
a chiamare il costruttore in doppione, prima quello di defalut 
poi quello voluto.
Oppure come fare ?
ciao
Soviet


 Oppure come fare ?
Come ho appena detto.
Kiuhnm

 hai scritto te nella funzione?
Senza sapere cosa vuoi fare daverro, non posso dire. Perche
l'allocazione dinamica, da essempio. Perque non semplicemente:
    std::vector< Oggetto > v ;
    for ( int i = 0 ; i < 5 ; ++ i ) {
        v.push_back( i ) ;
    }
se bene:
    std::vector< Oggetto > v( boost::counting_iterator( 0 ),
                              boost::counting_iterator( 5 ) ) ;
senza funzione, e senza ogni allocazione dinamica.


 (&o)[1]->mia_funzione() << endl;
Solo... chi scrive il C++ così? In generale, se s'appetta che il
cliente ha bisogna dell'indirizzo, non si serva di un
riferimente. Se deve fare un delete, da essempio. O se un
puntatore a un array, e non a un scalare.
È possibile fare un riferimente a un array, qualcosa come:
    Oggetto* (&array)[ 5 ] = ... ;
e allora, si scrive anche:
    array[ i ]->mia_funzione() ;
Solo, la sintassa per cuì è particolaremente pesante. Allora,
serva quasi soli nei templates (dov'è l'induzione dei tipi
s'occupa del sintassa---o perche voliamo che l'induzione dei
tipi ci da la lunghezze).









È ancora una ragione di utilisare vector:
    std::vector< Oggetto > v( iteratore_begin, iteratore_end ) ;
Invecci, se l'oggetto qui a una semantica con identità, bisogna
sempre utilisare i puntatori.


 Oppure come fare ?
Io. Mi servo di std::vector, e così, non ho questi problemi.


 Solo... chi scrive il C++ così?
Non a caso Alberto ha incontrato problemi usando i riferimenti in questo 
modo.
Penso che sia comunque istruttivo dedicare un po' di tempo a queste cose 
senza scandalizzarsi troppo. E' così che si fanno interessanti scoperte 
(v. template) o si approfondisce la conoscenza del linguaggio.
Kiuhnm

Grazie a tutti per il supporto :)
Alberto