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

Potete aiutarmi?
Perche' se nel file "*.h" viene definita la classe:
class clista {
	int elemento;
	clista* next;
public:
	clista(int,clista*);
             .......
};
clista::clista(int first,clista* inizio) {
    lista.elemento = first;
    lista.next = NULL;
    inizio = &lista;
}
E se nel file "*.cpp" (con include "*.h") c'e' la porzione di codice:
......
int first;
clista* inizio;
cout<<"Lista vuota\n";
cout<<"Inserire primo elemento:";
cin>>first;
clista lista(first,inizio);
......
Mi da l'errore che 'lista' (nel costruttore) non è definito?
grazie

Ciao,
    in effetti la parola "lista" appare per la prima volta nel corpo del
costruttore senza essere definita da nessuna altra parte! Per forza non la
trova. Visto che il secondo parametro del costruttore è un puntatore di tipo
clista dovresti usare quello e scrivere "inizio" dove hai scritto "lista".
Cmq non è convincente neanche il codice del file .cpp ma di questo credo sia
meglio parlarne quando avrai risolto i tuoi dubbi sul costruttore (una cosa
alla volta visto che da quanto ho capito sei agli inizi :-)
A presto :-9
Marco Abis
http://www.programmazione.it
  il sito Italiano di riferimento per IT
ab...@programmazione.it

> Ciao Giacomo.
Innanzi tutto un consiglio: limita il contenuto dei tuoi file *.h alle
dichiarazioni, e metti le definizioni rigororsamente nei *.cpp. Avrai
molti meno problemi in futuro ;)
Riguardo al codice che hai mandato, credo di aver capito che il puntatore
clista* tra i parametri del costruttore ti serve per poi accedere alla
lista. Questo è un modo di programmare che 'sa' molto di strutturale (C e
pascal), ma aderisce poco al modello di programmazione a oggetti. Ti
consiglio di approfondire molto bene questi argomenti, prima di lanciarti
con il codice C++ :
1) differenza tra classe e istanza;
2) relazione Is-A
3) relazione Has-A
4) polimorfismo
Questi argomenti diventano semplici solo quando li hai capiti veramente,
e per capirli non hai bisogno del c++ (se proprio non riesci a stare
lontano dal compilatore, forse per cominciare Java va meglio). Un utile
esercizio consiste nel provare a scomporre una realtà semplice secondo il
modello a oggetti. Per chi proviene dai linguaggi strutturati questo modo
di vedere le cose è spesso molto difficile da acquisire... ma alla lunga
paga!
Comunque per compilare la tua classe, il costruttore ha il seguente body:
    elemento = first;
    next = NULL;
    inizio = this;
...e ricordati di tenere le definizioni nei file *.cpp!
Ciao.
Roberto Cavada

 Mi da l'errore che 'lista' (nel costruttore) non č definito?
Non discuto del significato di ciň che hai scritto...
Attenendomi esclusivamente alla semantica del linguaggio
posso dirti che:
1) se nel corpo del costruttore vuoi far riferimento ai membri dell'istanza
corrente
della tua classe (l'oggetto in fase di inizializzazione), usa semplicemente
i loro
identificatori, e.g.:
elemento = first;
next = NULL;
Eventuali conflitti con variabili locali omonime si risolvono specificando
l'oggetto a mezzo del parametro <this>:
this->elemento = first;
this->next = NULL;
Statements facilmente comprensibili se si considera che
le funzioni membro non-statiche possiedono un parametro addizionale,
passato automaticamente loro all'atto della chiamata:
<this>, il puntatore all'oggetto che ha invocato il metodo.
2) Probabilmente con l'espressione <&lista> volevi riferirti
all'indirizzo dell'oggetto che stai creando: di nuovo, usa <this>.
Il nome di un oggetto (<lista> in questo caso) non appartiene allo 'scope'
delle sue funzioni membro (non-statiche)...
3)  <inizio> č uno dei parametri formali del costruttore, una variabile
locale che contiene
un puntatore ad un oggetto di classe <clista>.
Se era tua intenzione assegnare l'indirizzo dell'oggetto <lista>
alla variabile <inizio> definita nell'ultima porzione di codice, puoi
passarla per riferimento (e non per valore) al costruttore,
modificando semplicemente la dichiarazione del parametro omonimo
nella lista dei parametri del costruttore:
usa il tipo <clista *&> anziché, come riporti , <clista *>
... ma forse sarebbe piů semplice assegnare l'indirizzo dell'oggetto, dopo
averlo istanziato, con lo statement seguente:
<inizio = &lista;>
da aggiungersi come ultima riga all'ultimo segmento di codice.
In tal caso il secondo parametro del costruttore diviene superfluo.
Adriano.

Mi da l'errore che 'lista' (nel costruttore) non è definito?
Perchè lista non è nè una variabile membro della classe clista nè un
parametro del costruttore e quindi quest'ultimo, quando cerchi di accedervi,
non la trova.
Ciao, Giuseppe
---------------------------------------------------------------------------
_Guybrush su IRCNet canale #programmazione
ICQ: 42227764
---------------------------------------------------------------------------

Grazie a tutti!!!
Un'ultima cosa:
relazione Is-A
relazione Has-A
Dove posso trovare documentazione?( ho un libro ed il tutorial di
Paolo Marotta ma non ne avevo mai sentito parlare)
grazie ancora

 Paolo Marotta ma non ne avevo mai sentito parlare)
Thinkink in C++ di Bruce Eckel all'indirizzo
ftp://mindview.net/pub/eckel/TIC2e
.
EMG

Salve ancora.
Sul libro ho visto un esempio di programm. a oggetti.
Non riesco pero' a capire questa porzione di codice:
NNR e' la classe genitore mentre administration e' la classe
figlia(ereditata public).
administration(char *lname,char *fname,char *ss,char *job,double
y_hired,double y_salary):NNR(lname,fname,ss,job,y_hired)
{
   ......
}
administration():NNR()
{
    .....
}
Che significano i due punti?
grazie

Paolo Marotta ma non ne avevo mai sentito parlare)
Ne accenno appena nell'appendice B...
credo che cerchero` di trasformarla in un capitolo (tempo permettendo).
Paolo
-----------------------------------------------------------------
  Paolo Marotta
  Computer Science, University of Pisa
  home:   
http://www.cli.di.unipi.it/~marotta
-----------------------------------------------------------------

 grazie
Supponiamo di avere due classi in relazione is-a (B is-a A). Allora
un'istanza di B è anche un'istanza di A. Quindi se crei un'istanza di B,
ad esempio in questo modo:
    B b;
...puoi immaginare che vengano create prima un'istanza di A, e poi
quella vera e propria di B, che "contiene" A. Allora vengono invocati
prima il costruttore di A (quello di default, senza parametri), per
creare l'istanza di A "all'interno" di B, e poi il costruttore (senza
parametri) di B, al fine di costruire l'istanza completa <b>. Tutto
questo avviene senza dover dire niente al fedele compilatore.
Ora supponiamo che la classe A voglia uno o più parametri per poter
essere creata. Così come stanno le cose il compilatore chiama di default
il costruttore vuoto di A (che potrebbe non esistere, e che comunque non
è adatto a creare un'istanza di A come vogliamo noi). Allora non ci
resta che passare al costruttore di B gli uno o più parametri che
servono al costruttore di A, e "dire" al costruttore di B di passarli
poi al costruttore di A. Ho detto qui sopra però che il costruttore
delle classi base (o super classi) viene chiamato PRIMA del costruttore
delle classi derivate, quindi il costruttore di B non ha modo di passare
questi parametri, se non con una sintassi particolare che prevede l'uso
dei due punti.
Nel tuo caso:
administration(char *lname,char *fname,char *ss,char *job,double
y_hired,double y_salary):NNR(lname,fname,ss,job,y_hired)
{
   ......
}
è un costruttore che passa alcuni parametri al costruttore base (che
evidentemente li usa per potersi costruire). L'ultimo parametro, non
passato a NNR, viene utilizzato dalla classe derivata (detta anche
estesa, in una terminologia più recente).
Il caso:
administration():NNR()
{
    .....
}
è evidentemente il caso di overloading dei costruttori per avere un
costruttore vuoto (di default). In questo caso però il compilatore
invoca automaticamente il costruttore vuoto della classe derivata (NNR),
e quindi non è necessario specificarne la chiamata nel costruttore
administration(). Il costruttore vuoto (se presente) viene chiamato ogni
volta che non ne viene espressamente chiamato un altro. Non mi ricordo
se faccio confusione con Java, ma sono abbastanza sicuro che se viene
definito un costruttore con parametri, allora il compilatore non mette
più a disposizione il "suo" costruttore di default per le classi, che
quindi deve essere espressamente dichiarato e definito.
L'uso della forma estesa nella dichiarazione del costruttore derivato
viene utilizzato naturalmente anche nel caso di ereditarietà multipla
(che ti consiglio caldamente di evitare accuratamente, almeno per
qualche mese ancora). La sintassi in questo caso prevede un elenco di
costruttori parametrizzati delle classi base, separati da virgole.
Molto più interessante è l'altro significato che il compilatore dà alla
sintassi due-punti  nella dichiarazione del costruttore, utilizzata
anche in presenza di forme has-a.
Supponiamo di avere una classe B, che per essere costruita ha bisogno di
un parametro (mettiamo pure un <int>). Supponiamo poi di avere una
classe A, che ha tra i suoi membri (privati o protected) un'istanza
della classe B, che con grande fantasia chiamiamo <b>. Ecco la nostra
classe A:
1    class A {
2       private:
3          B b;
4        public:
5            A(int iPar_b) {  //il parametro è per <b>
6                ...
6+m           } //A(int)
7+m    }; //class
Il problema è che b, per poter essere creato, necessita di un int, e si
dia il caso che queso int venga fornito nel costruttore di A, al momento
della creazione di un'istanza <a>. Tuttavia per come ho scritto le cose
qui sopra, b viene creato subito, nel momento della creazione di <a>,
utilizzando il costruttore vuoto di B  (vedi 3). Come fare a dire al
compilatore di aspettare a creare b, in modo da passare al suo
costruttore parametrizzato il parametro iPar_b, creando quindi b con il
costruttore B(int) anzichè con B()?? Ci sono due soluzioni al problema.
La prima fa uso della sintassi estesa della dichiarazione del
costruttore (in questo caso di A):
(...)
(sostituire 5)  A(int iPar_b) : b(iPar_b) {
(...)
Questa sintassi dice al compilatore come gestire l'inizializzazione del
membro <b>. Essa è utile anche per assegnare direttamente un valore a
delle variabili di tipo primitivo, in questo modo ad esempio:
    class A {
        private:
            int iA;
            char cA;
        public:
            A(int _iA, char _cA) :
                iA(_iA), cA(_cA)
                { //corpo del costruttore... }
    }; //class
...naturalmente in questo caso non ci sono differenze con questo codice:
    class A {
        private:
            int iA;
            char cA;
        public:
            A(int _iA, char _cA)  {
                //inizializza qui le variabili:
                iA = _iA;
                cA = _cA;
                //corpo rimanente del costruttore...
                } //A(int, char)
    }; //class
..dicevamo che ci sono due metodi per risolvere l'inizializzazione di un
membro al momento della chiamata del costruttore della classe che lo
contiene. Il secondo metodo salta a pié pari il problema, rendendo
dinamica la creazione del membro, che quindi non sarà più un'istanza
(nel nostro esempio <B b>) ma un puntatore all'istanza (cioè <B*  b>).
La classe A diventa:
1    class A {
2       private:
3          B* pB;
4       public:
5            A(int iPar_b) {  //il parametro è per <b>
6                pB = new B(iPar_b);
7             } //A(int)
8           ~A() {
9              delete pB;
10            } //~A()
11     }; //class A
Alla riga 6 costruisci dinamicamente un'istanza di B, e vi accederai
attraverso pB.
Ora però è necessario un distruttore (riga 8), per evitare morti in giro
per la memoria.
Quale preferire tra i due metodi? Facile,entrambi! Purtroppo è così ,
visto che dipende da caso a caso... anche se giocare con la memoria è
una delle cose che più piace ai programmatori C[++], quando si può è
meglio far fare al compilatore: meno grattacapi in futuro (anche questa
è ingegneria del software).
Ciao.
Roberto Cavada.

 grazie
I due punti identificano l'inizio della _lista_di_inizializzazione _:
in essa è possibile richiamare i costruttori delle classi base
e di tutti gli oggetti membro.
I due frammenti di codice sono le definizioni di due costruttori
della classe <administrator>.
Quando erediti una classe (derivata) da un'altra (base),
un costruttore (qualunque) della classe derivata può richiamare
un costruttore della sua classe base al fine di inizializzare
la porzione da essa derivata...
Ciò non toglie che si possano inizializzare le variabili membro
della classe base esplicitamente, ma il primo costituisce
un mezzo più elegante, spesso più efficiente, sicuramente
più corretto dal punto di vista della OOP.
Spero di essere stato chiaro.
Il discorso è cmq complesso.
Perciò... studiati bene il 'capitolo' dei costruttori:
è conveniente a tal proposito avere un buon testo
a portata di mano.
Quello di Bruce Eckel è uno dei migliori che mi sia mai
capitato di leggere: come se non bastasse è pure
distribuito gratuitamente sulla rete (personalmente
spero di acquistare quanto prima la versione cartacea:
amo i libri tradizionali!)
Prova a cercare partendo da questo indirizzo
(vado a memoria): 
http://www.eckelobjects.com/
Adriano.

 grazie
I due punti identificano l'inizio della _lista_di_inizializzazione _
(peraltro facoltativa): essa costituisce parte della definizione
di un costruttore, laddove è possibile richiamare i costruttori
delle classi base e di tutti gli oggetti membro della classe in
esame...
I due frammenti di codice sono, appunto, le definizioni di due
costruttori della classe <administrator>.
Quando erediti una classe (derivata) da un'altra (base),
un costruttore (qualunque) della classe derivata può richiamare
un costruttore (qualunque) della sua classe base al fine di inizializzare
la porzione da quest'ultima derivata...
Ciò non toglie che si possano inizializzare esplicitamentele variabili
membro della classe base , ma il primo costituisce
un mezzo più elegante, più compatto, più efficiente, sicuramente
più corretto dal punto di vista della OOP...
In tal modo, infatti, eviti di riscrivere codice già disponibile e
pronto all'uso (quello di un costruttore - il più opportuno - della classe
base)
tenendo anche conto che in caso di assenza di una chiamata esplicita ad un
costruttore, in fase di istanziazione di un oggetto della classe derivata,
viene chiamato il costruttore_di_default della classe base:
le inizializzazioni portate a termine da quest'ultimo possono non
coincidere con quelle da noi desiderate... salvo (ri)assegnare valori
idonei nel corpo del costruttore della classe derivata!!...
Naturalmente questa non costituisce una regola ferrea:
esistono eccezioni, ma vanno valutate di volta in volta...
Spero di essere stato chiaro.
Il discorso è cmq complesso.
Perciò... studiati bene il 'capitolo' dei costruttori:
è conveniente a tal proposito avere un buon testo
a portata di mano.
Non so quale adoperi ma mi sento di consigliartene uno:
purtroppo è solo in Inglese (al momento)...
L'autore è Bruce Eckel e il testo in questione è uno dei migliori
(il migliore?) che mi sia mai capitato di leggere:
se non bastasse... è pure distribuito gratuitamente sulla rete!!!
(personalmente spero di acquistare quanto prima la versione cartacea:
amoi libri tradizionali!).
Attenzione: si tratta di una versione non definitiva!
Bruce continua tuttora ad ampliare/migliorare l'opera:
stiamo assistendo in diretta
alla nascita di un capolavoro... :,,-)) (lacrime di commozione)
Prova a cercarlo partendo da questo indirizzo



Posso solo aggiungere a quanto già scritto da Paolo Marotta che nell'ottimo
testo di James O. Coplien "Advanced C++ Programming Styles and Idioms" è
presente un ampia trattazione dell'argomento, distribuita lungo diversi
capitoli. Imperdibile, imho, il capitolo 6 (Object Oriented Design) e il
capitolo 7 (Reuse and Objects). Nel contesto della discussione, evidenzierei i
sottocapitoli "Object and Class Relationship", "Subtyping, Inheritance and
Forwarding" e "Rules of Thumb for Subtyping, Inheritance and Independance" per
il primo, "Reuse, Inheritance and Forwarding" per il secondo (anche se,
probabilmente tutto il capitolo è degno di menzione).
Ciao
AB
--
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Andrea Baruzzo
Via Circonvallazione, 26
Muzzana del Turgnano (UD) - Italy
e-mail: 
andrea....@ud.nettuno.it
~~~~~~~~~~~~~~~~~~~~~~~~~~~~