Newsgroup: it.comp.lang.c++
Titolo: Dove sbaglio?

Ciao a tutti,
ho iniziato da qualche giorno a studiare il c++ (è il primo linguaggio 
che studio). Ho fatto un programmino che gestisce una lista. Tale lista 
gestisce tipi diversi a seconda del parametro di template passato 
durante l'istanziamento (non so nemmeno se sto usando i termini giusti 
:P)..fra i metodi che ho implementato ho scritto l'overloading 
dell'assegnamento in modo da poter assegnare una lista di un tipo 
qualsiasi ad un'altra di un altro tipo qualsiasi. Il compilatore però mi 
dà 2 errori che non riesco proprio a capire (gli altri 2000 che mi dava 
sono riuscito a correggerli :)).
Innanzitutto, si può fare quello che sto provando a fare?
Provo a postare il codice sperando che qualcuno ci voglia dare uno sguardo:
QUESTO E' IL GENERICO ELEMENTO DELLA LISTA
template<typename T>
class  Nodo {
	private:
		friend class ListaBase<T>;
		Nodo<T> *succ;
		Nodo<T> *prec;
		T elemento;
	public:
		Nodo (T el, Nodo<T> *s=0,Nodo<T> *p=0): elemento(el), succ(s), prec(p){};
};
QUESTA E' LA CLASSE CHE DESCRIVE LA LISTA
template<typename T>
class ListaBase {
	protected:
      	Nodo<T>  *primo,*ultimo;
		int len;
	public:
		ListaBase(){ primo=ultimo=NULL;len=0;}
		void InserisciInTesta (T );
		void accoda(T);
		void CancellaInTesta();
		void CancellaInCoda();
		void DistruggiLista();
		void PosizioneElemento(T );
		~ListaBase(){DistruggiLista();}
		void stampa();
		int lunghezza();
		void operator= (Lista<T>, Lista<T>);
};
QUESTO E' IL METODO CHE MI GENERA L'ERRORE
template <typename T>
void ListaBase<T> :: operator= (Lista<T> L, Lista<T> C)
{
     Nodo<T> *p1=L.primo;
     Nodo<T> *p2=C.primo;
     while (p2->succ != NULL)
      {
          if (p1->elemento == NULL)
          {
              Nodo<T> p1 = new Nodo<T>;
          }
          reinterpret_cast<typeid(p2->elemento)> (p1->elemento);
          p1->elemento = p2->elemento;
          p1 = p1->succ;
          p2 = p2->succ;
      }
}
gli errori del compilatore sono:
- ` void ListaBase<T>::operator=(Lista<T>, Lista<T>)' must take exactly 
one
- syntax error before `typeid'
grazie in anticipo a chi voglia aiutarmi
Phate
P.S. scusate la lunghezza del post.

 qualsiasi ad un'altra di un altro tipo qualsiasi.
suggerisce che tu abbia le idee piuttosto confuse su come funzionino i
template e su come e quando sia possibile effettuare il casting da un tipo
a un altro. 
Max

 coinvolti sono esattamente dello stesso tipo.
provo a spiegarlo a parole visto che forse il codice è talmente 
sbagliato da non farlo capire. Ho una classe Lista definita come 
template ( Lista<T> ). Ora ammettiamo, ad esempio, che io istanzi 3 
liste, una che gestisca interi, l'altra che gestisca numeri complessi e 
l'altra che gestisca tessere (Lista<int> lista1, Lista<complesso> 
lista2, Lista<tessera> lista3).
Vorrei fare in modo che scrivendo lista1 = lista2,  lista1 diventi una 
lista di complessi e contenga gli stessi elementi di 
lista2..analogamente scrivendo lista1 = lista3 o lista2 = lista3.
Ho letto sul manuale che reinterpret_cast serve per convertire un tipo 
in un altro anche completamente diverso e che typeid serve per conoscere 
run-time il tipo di una determinata variabile; per questo li ho usati in 
quel modo.
grazie
Phate

Phate ha scritto:
[ CUT ]
Scusa, magari a te serve anche, ma da un punto di vista semantico, a che 
diavolo ti serve ?
Non sarebbe meglio creare una funzione template per questa feature ( 
comunque discutibile ) lasciando da parte l'operatore = ??
Oppure, non sarebbe meglio che creassi altresě un metodo Clone() che 
invocato su una lista te ne restituisca un clone,  appunto ???
L'operatore = utilizzato per scopi di questo tipo - ripeto - forse non 
e' la scelta migliore e soprattutto abbatte a 0 la leggibilita' 
dell'istruzione...
IMHO, ovviamente.

 che studio).
Ti hanno gia' risposto gli altri, mi limito a dirti che:
tu affermi di non aver mai studiato un linguaggio di programmazione e
quindi non aver mai programmato prima.
da *qualche giorno* stai studiando il C++, il tuo primo linguaggio, e
gia' stai a fare programmi con classi, templates, overloading operatori,
lista gestita a mano etc...
Il tuo programma ha parecchi errori seri, e' vero, e questi dimostrano
che non conosci il C++ abbastanza bene. Ad ogni modo, nonostante gli
errori, rimango dell'opinione che:
- o hai un concetto del tempo strano e qualche giorno per te significa
tipo un mese o due.
- oppure, se veramente e' "qualche giorno" allora complimenti e di
questo passo fra 2 giorni, anche senza chiedere aiuto a noi, sarai in
grado di risolvere il problema da solo e fra altri 2 giorni sarai tu ad
aiutare noi sul NG.
- oppure ancora il codice e' al 90% un copia incolla (tranne nelle parti
sbagliate).
- o hai studiato un libro tipo: "learn C++ in 1440 minutes" che si
finisce in 3-4 giorni (24 ore di studio), ma quando lo finisci hai solo
in testa una lista di cose che si possono fare ma non sai in realta'
fare nulla. Hai una gran confusione in testa ed enormi lacune. Ad
esempio, sai +o- cosa sia un template ma non sai cosa serva e come
realmente si usi.
Tutto questo solo per dire che di solito la gente in qualche giorno
impara la struttura di "hello world", gli if, i cicli, input/output base
e ad usare qualche classe della libreria standard come vector e string.
Tutto questo per fare programmi banali in una unica funzione.
Insomma se hai veramente iniziato da qualche giorno c'e' qualche cosa
che non va:
- o hai letto un libro poco rigoroso, nel qual caso comprane uno
migliore.
- o hai letto troppo in fretta un libro rigoroso, sfogliando le pagine
in pratica, senza studiarlo per bene.
- o stai facendo copia-incolla o scopiazzando altri codici senza capirli
fino in fondo.
- o stai cercando di fare cose troppo complesse per il tuo livello,
imparati prima bene le basi.
Tommaso
-- 
Posted via Mailgate.ORG Server - 
http://www.Mailgate.ORG

 
provo a spiegartelo anche se uscirà qualcosa di molto contorto.
Dopo aver creato le varie classi e i vari metodi del mio "progetto" ho 
scritto un programmino per collaudarne le funzionalità.
All'inizio del programma l'utilizzatore ha la possibilità di lavorare su 
una lista di complessi o su una lista di interi (ovviamente avendo 
definito la lista come template è in grado di lavorare su qualsiasi tipo 
io le passi..però mi limito a questi due). Dopo aver scelto uno dei due 
tipi di lista, l'utente carica gli elementi nella lista e 
successivamente compare un altro menu dal quale scegliere le varie 
operazioni che si possono applicare alla lista (cancella elemento, 
inverti, stampa, lunghezza ecc ecc).
Il codice a questo punto è una cosa del genere:
cout<<"Scegli operazione: ";
cin>>scelta;
switch (scelta)
{
    case '1': //scelta dell'operazione 'inverti'
        L.inverti(); //L è la lista di interi
        break;
    case '2': //scelta dell'operazione 'stampa'
        L.stampa();
        break;
    ecc ecc...
}
questo pezzo di codice serve nel caso in cui venga scelto di lavorare su 
una lista di interi (L). Per la lista di complessi (C) sono costretto a 
riscrivere il pezzo di codice una seconda volta:
cout<<"Scegli operazione: ";
cin>>scelta;
switch (scelta)
{
    case '1': //scelta dell'operazione 'inverti'
        C.inverti(); //C è la lista di complessi
        break;
    case '2': //scelta dell'operazione 'stampa'
        C.stampa();
        break;
    ecc ecc...
}
cambiando solo L in C. E sono costretto a riscrivere questo pezzo di 
codice per ogni tipo di lista che l'utente può scegliere.
Quello che vorrei fare io è scrivere il codice che contiene lo switch 
solo per la lista L e fare in modo che tale lista L sia una lista di 
interi, di complessi o di qualunque altro tipo a seconda della scelta 
dell'utente.
Per questo avevo pensato di ridefinire l'assegnamento: l'utente sceglie 
ad esempio la lista di complessi (C), la carica con i vari elementi e 
poi l'istruzione L = C mi permette di entrare nell'unico switch scritto 
per la lista L (che nel caso specifico non è più una lista di interi ma 
una lista di complessi..L = C).
Si è capito qualcosa?..Se mi mandi a quel paese sei più che giustificato :P
Phate

 Tommaso
ciao
Phate

Phate ha scritto:

Stai puntando troppo in alto. Non perche' il tuo programma sia 
difficile, ma perche' dovresti avere degli accorgimenti di natura 
progettuale: devi realizzare un meccanismo in grado di astrarre 
l'operazione dal tipo. Polimorfismo.
Inoltre hai introdotto i template, altra cosa rognosa da gestire.
Potresti pensare ad un command pattern, ma ti ripeto, dovresti:
a) conoscere bene il linguaggio
b) conoscere principi di pattern design
c) creare una tua soluzione ad hoc.
Capisco che applicarsi su problemi di questo tipo e' divertente e 
stimolante, ma credo ti manchi ancora qualche concetto.
Cerca di fare le cose per gradi.
Fai una classe base virtuale (es.: Struttura ), con i metodi che ti 
servono ( virtuali puri ).
Poi derivi la classe e la specializzi nella struttura che serve a te ( 
StrutturaInteri, StrutturaComplessi ) specializzando i metodi virtuali 
puri a secondo del caso.
Fatto questo, e' evidente che ti trovi nel caso in cui se hai bisogno di 
una nuova tipologia di struttura devi scrivere del codice, allora 
potresti utilizzare i template. A questo punto, pero', ti rendi anche 
conto che le strutture che te vuoi 'interscambiare' tramite l'uso 
dell'operatore '=' *non* sono completamente equivalenti ( anche se parli 
di strutture di interi e complessi potrebbe essere un problema: su un 
complesso potrai utilizzare il metodo Re(), che ti torna la parte reale, 
su un intero che senso ha avere Re() ?? ). Per cui forse non puoi avere 
una gerarchia di classi con una unica classe base *se vuoi essere in 
grado di utilizzare late binding* . Allora cominci a vedere qualche 
pattern e ti rendi conto che questi problemini hanno tutte delle loro 
tecniche di risoluzione, che devi scegliere volta per volta.
Spero che hai capito il senso del mio messaggio. Ciao !!!

> scrivendo lista1 = lista3 o lista2 = lista3.
Da questa frase invece e' evidente che tu non sappia a cosa servano i
templates e come si usino.
Posso chiederti che libro stai leggendo. Esperienza insegna che spesso
errori come quelli che hai fatto sono causa di un libro di testo non
valido.
A mio avviso per imparare abbastanza C++ da poter scrivere codice tipo
quello che stai cercando di scrivere ti servono almeno due mesi di
studio su di un testo molto valido, altrimenti scrivi cose senza senso
come il metodo riportato sopra.


 lista2..analogamente scrivendo lista1 = lista3 o lista2 = lista3.
non so se ciò che scrivo ti accenderà una lampadina, ma template 
  = compile time (come una macro evoluta) e non = runtime.
Questo significa che la flessibilità è tutta a compile time 
(puoi istanziare i tipi che ti pare, ma una volta istanziati hai 
creato un nuovo tipo, e per comvertire tipi tra loro ignoti al 
compilatore e distinti e perennemente differenti, devi scrivere 
costruttori incrociati ed operatori di conversione espliciti 
incrociati ed assegnazioni incrociate (in altre parole funzioni 
che prendono un valore lista<int> e ne costruiscono un 
lista<tessera>, che per il compilatore sono scatole nere 
DIVERSE, staticamente diverse, non castabili ne convertibili in 
altro modo. Il compilatore può fare conversioni in altri 
contesti, ad es. convertire oggetti di classi derivate ad ognuno 
dei tipi base, diretti o remoti, tagliando qualche fetta 
dall'oggetto derivato che è un sovrainsieme).
Di conseguenza per far girare lista1 = lista3
devi scrivere
template <class Sorg, class Dest> Dest Converti (Sorg & Sorgente)
[
Dest Temp;
// converti Da Sorgente a Temp a mano come credi
return Temp;
];
ed istanziare questa funzione template ad es. facendo
Lista1 = Converti<Lista<int>, Lista<Tessera>> (Lista3);
ho penato a una famiglia di funzioni esterne, non membro, per 
limitare l'interfaccia interna .... tuttavia è chiaro che queste 
funzioni membro sono strettamente connesse ad entrambi i 
dettagli interni di entrambe le classi di origine e 
destinazione, perché devono prendere un cubo e ficcarlo in un 
contenitore a cilindro, e quindi devono sapere esattamente tutto 
al riguardo. Se tocchi un membro della classe dovrai rivedere il 
convertitore.
Sintassi a parte, c'è un altra difficoltà concettuale che per il 
compilatore è ardua da trattare volendo sperare in un cast 
automatico : il fatto che tu, oltre ai puntatori precedente e 
successivo, il contenuto lo istanzi dentro per valore (ossia 
riservando area dati ... ciò è comodo, ma si ricade nel discorso 
di contenitori poco astratti e di capienza specifica). Volendo 
progettare due container sì tipizzati ma che si prestino a cast 
semplici, io avrei messo non gli oggetti dati interi alla lista 
bensì puntatori agli stessi. Infatti i puntatori a qualsiasi 
cosa hanno cmq tutti le stesse dimensioni, ed il cast, dove 
possibile, può essere effettuato più facilmente ed 
efficientemente. Dove non è possibile castare, è ininfluente 
usare l'oggetto vero o solo il puntatore. Il prezzo è dovere 
allocare memoria con NEW, perché il dato non risiede fisicamente 
nell'oggetto di lista ma è solo collegato.
ciao
Soviet_Mario


 Tommaso
ciao
Phate

 speravo un po' meno..comunque avrai di certo più argomenti di me per dirlo.
Se ti impegni davvero molto puoi fare prima, ma ti serve un *buon* libro
e tante tante ore al giorno. ;-)
(questo ad imparare abbastanza bene il C++ e non aver problemi a
scrivere codice come quello che stai scrivendo; non da impararlo
veramente bene, questo richiede anni).


 reinterpret_cast<typeid(p2->elemento)> (p1->elemento);
nella fretta mi sono dimenticato di menzionare che l'uso di typeid e'
errato.



ciao e grazie
Phate


Phate

 
Il risultato di typeid č un valore di tipo "type_info&", che puň essere 
adoperato in vari modi, ma di certo non come parametro in un operatore di 
cast, che deve essere invece il nome di un tipo.
Max

 non sa tanto di professionale, no?
Non lo conosco, quindi non posso giudicare. Tuttavia un libro che
insegna sia C++ che Java non mi ispira.
Quando poi consideri che e' per spiegare la OO, il C++ ed il Java
contiene solo 500 pagine...


 
CUT

 P.S. scusate la lunghezza del post.
Aggiungo solo qualche commento a quanto già detto nel resto del thread:
- L'overloading dell'operatore di assegnamento non è bene che ritorni
void, ma bensì un riferimento all'assegnato. Per esempio, una riga tipo:
x = y = z;
è considerata valida dalla maggior parte dei programmatori, ma per
funzionare richiede una bella segnatura di funzione tipo:
template<typename T>
ListaBase<T>&
ListaBase<T>::operator=(const Lista<T>& C)
l'ultima riga sarà tipicamente un bel "return *this".
- un operatore di assegnamento, di solito, è inteso come un operatore
che assegna tutto l'oggetto a destra dell'uguale a ciò che sta a
sinistra, e non solo parte di esso. Insomma, alla fine chi usa le tue
classi si aspetta ragionevolmente che se ha fatto (a = b), valga (a ==
b). Quello che ti serve è fare la "copia profonda" della lista,
allocando nuovi nodi. Altrimenti, per non buttare via un mucchio di
memoria, avrai bisogno di utilizzare dei metodi di condivisione della
memoria, ad esempio impiegando dei reference count. Trovi molto
materiale in merito all'interno della maggior parte dei corsi
universitari di C++, oppure sulla rete.
- perché Nodo è classe esterna a ListaBase? Meglio interna e
privata/protetta, non vogliamo che qualcuno vi acceda da fuori... fa
parte del principio di information hiding. Questo ti permette anche di
evitare la definizione di nodo come classe friend (che non trovo
comunque necessaria, uhm...)
template<typename T>
class ListaBase {
  class Nodo;
// [...]
protected:
  class Nodo {
  };
// [...]
};
- p1 è variabile locale (forse qualcuno l'aveva già fatto notare),
quindi alla fine del corpo della funzione se l'avevi modificata viene
"persa"
- Data la natura del progettino, credo che tu non voglia effettuare
conversioni "di lista", ma di [oggetti istanziati da] classi che questo
contenitore immagazzìna, il che è ben diverso. L'errore è abbastanza
concettuale: normalmente i contenitori non devono conoscere molto degli
oggetti che vengono loro affidati; devono essere indipendenti dal tipo
contenuto. Così funziona anche il template di classe "list" delle STL.
Forse il problema sarebbe più semplice definendo una classe Number,
anche senza metodi inizialmente, e poi scrivendo dei metodi di
conversione tra interi e complessi etc. che chiamerai da _fuori_ la
classe lista.
Puoi anche usare l'overloading degli operatori di conversione.
class Numero;
class Intero;
class Complesso;
// [...]
class Intero : public Numero {
  int _i;
public :
  Intero( int numero = 0 ) : _i(numero) {}
  virtual ~Intero() {}
  // etc.
  int get() { return _i; }
  void set( int numero ) { _i = numero; }
  operator int() { return _i; }
  operator Complesso() { return Complesso( _i, 0 ); }
   // etc.
}
// poi per esempio, potrai scrivere : Complesso c = Intero(5);
- evita il reinterpret_cast come la peste. In 999 casi su mille, se sei
costretto ad usare il reinterpret_cast c'è qualcosa che non va con il
tuo design. Questo permette di castare QUALSIASI cosa su QUALSIASI
altra, con evidenti problemi colossali (che senso ha castare la classe
Melone sulla classe Telefono per esempio? il c++ non è così magico da
capire cosa intendi dire, ci vuole del codice per farglielo intendere).
Usi più frequenti (ma non è questo il caso) potrebbero richiedere
piuttosto degli static_cast, che sono di gran lunga i più diffusi.
Infine consiglio (come sempre) l'Eckel come libro, "Thinking in C++". Lo
trovi qui per il download:
http://mindview.net/Books/TICPP/ThinkingInCPP2e.html
Leggi con cura specie il capitolo introduttivo sull'OOP in generale.
Spero di non aver detto troppe amenità perché stasera sono stanco morto.
Abbiate venia... ;-)
Ciao,
-- 
Matteo Settenvini
FSF Associated Member
Email : 
mat...@member.fsf.org
-----BEGIN GEEK CODE BLOCK-----
Version: 3.12
GCS d--(-) s+:- a-- C++ UL+++
P?>++ L+++>$ E+>+++ W+++ N++ o?
w--- O- M++ PS++ PE- Y+>++
PGP+++ t+ 5 X- R tv-- b+++ DI+
D++ G++ e h+ r-- y?
------END GEEK CODE BLOCK------

 P.S. scusate la lunghezza del post.
Aggiungo solo qualche commento a quanto già detto nel resto del thread:







template<typename T>
class ListaBase {


scusa ma tu dopo QUALCHE GIORNO che inizi il tuo primo linguaggio di
programazione già smanetti con template, liste e quindi con puntatori e
memoria dinamica?
O__O

 typeid č utilizzabile solo con gli oggetti?
leggi la risposta di Max. In sostanza puoi dire:
if(typeid(a) == typeid(b))
{
}
ad esempio, ma non puoi fare:
typeid(a) c;
Sperando che c sia una variabile dello stesso tipo di cui e' a.
Quindi il cast e' (anche) sbagliato per questo motivo. La ragione per
cui non si puo' fare e' spiegata da Max e ancora meglio da qualunque
guida (valida) che parli di typeid.
Il tuo libro cosa dice a riguardo?


 
Phate


il mio libro non lo nominava proprio. l'ho letto su un altro testo 
(Guida completa al c++..300 pagine sul C e 400 sul C++), ma mi ero 
fermato ai primi righi dove dicenva semplicemente che typeid può essere 
usato per determinare run-time il tipo di un determinato oggetto..non 
ero sceso nei dettagli :)
Mi sa che devo mettermi a leggere bene questo testo se non voglio fare 
altre figure come quella che ho fatto qui.
A proposito, l'hai mai sentito nominare questo testo?
Phate

 A proposito, l'hai mai sentito nominare questo testo?
Si, e' il testo di Schildt, vero? Schildt e' molto criticato e per
validi motivi.
Il testo che hai tu, in particolare, contiene molti meno errori di altri
testi di Schildt, ma ha un grosso problema:
- insegna il C++ partendo dal C. Cosa che aveva senso parecchi anni fa,
ma ora non ha piu' alcun senso.
Tutto sommato e' un libro piuttosto semplice e leggibile, ma non e'
sicuramente un testo che ti consiglierei.
Insomma credo tu abbia a mano due libri, uno meno adatto dell'altro. Uno
e' una introduzione affrettata a java e C++, l'altro e' un libro scritto
da un autore molto criticato per i sui errori di fondo e che usa un
l'approccio da inizio anni '90 , ora sbagliato, di insegnare prima il C
poi il C++.
Per cio' che riguarda "le figure"... Non hai fatto nessuna figura.
Programmare e' difficile e richiede tempo e ci siamo passati tutti. Io
ho fatto figure simili per parecchio tempo ed ora, in genere su piani
diversi, mi capita ancora di farle. Quindi e' normale.
Se sai l'inglese ti consiglio:
"C++ Primer, 4th edition".
E' un libro piuttosto completo, scritto molto bene ed in chiave moderna
(la 4a ed e' del 2005).
Sono 900 pagine di solo C++ (mi pare).
Se preferisci in Italiano esiste la traduzione della 3a versione che e'
comunque valida. Tuttavia e' un peccato leggere una edizione indietro (e
le traduzioni in genere contengono errori).
Altrimenti prova con questo, sperando abbiano fatto un buon lavoro nella
traduzione: 
http://pensareincpp.altervista.org/
E' gratis, puoi scaricarlo da li, (e' gratis anche la versione inglese)
ed e' un testo certamente migliore di quelli che hai ora (e' un ottimo
testo, uno dei migliori, anche se preferisco C++ Primer che a mio avviso
ha un target piu' vasto.).
Il libro di Stroustrup (creatore C++) te lo consiglio invece fra un
pochino di tempo. E' ottimo, ma lo consiglio sempre come seconda
lettura, anche se in teoria potrebbe essere letto anche come prima.


 C++ (mi pare).
Credo che "Accelerated C++: Practical Programming by Example" è
bene anche. È più corte, mà hò sentito che fà una
buon'introduzione à C++.
In ogni caso, credo che il primo libro non può essere che
un'introduzione, tanto il linguaggio è complicato. Si vuol dopo
libri più detagliati, da una parte, e dall'altra, libri sulla
programmazione in generale: la programmazione strutturata, OO,
algoriti...
--
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