Newsgroup: it.comp.lang.c++
Titolo: Problemi con l'overloading di <<

Ciao,
ho due classi:
Persona.h
class Persona
{
public:
	friend ostream& operator<<(ostream&, const Persona&);
	Persona(string nome, string cognome);
	virtual ~Persona();
	void setNome(string nome);
	void setCognome(string cognome);
	string getNome() const;
	string getCognome() const;
protected:
	string nome;
	string cognome;
};
Persona.cpp
string Persona::getNome() const{
	return nome;
}
string Persona::getCognome() const{
	return cognome;
}
ostream& operator<<(ostream& os, const Persona& x){
	os << x.getNome() << " " << x.getCognome();
	return os;
}
Utente.h
class Utente : public Persona
{
public:
	friend ostream& operator<<(ostream&, const Utente&);
	Utente(int codice,string nome, string cognome, string indirizzo, string
cap, string comuneResidenza, string provinciaResidenza, string telefono);
	virtual ~Utente();
	string getIndirizzo() const;
	string getCap() const;
	string getComuneResidenza() const;
	string getProvinciaResidenza() const;
	string getTelefono() const;
	int getCodice() const;
private:
	int codice;
	string indirizzo;
	string cap;
	string comuneResidenza;
	string provinciaResidenza;
	string telefono;
};
Utente.cpp
<tutti i vari getters con il modificatore const, come in persona>
ostream& operator<<(ostream& os, Utente& u){
	os << u.getIndirizzo();
	return os;
}
Il problema nasce a questo punto:
	Persona p1("b","m");
	Utente u2(2,"a","b","c","d","e","f","333");
	cout << p1 << endl; // tutto ok, stampa nome e cognome
	cout << u2 << endl; // il compilatore mi segnala un errore:
g++  -o"myapp"  ./src/myapp.o
./src/myapp.o:myapp.cpp:(.text+0x508): undefined reference to
`operator<<(std::basic_ostream<char, std::char_traits<char> >&, Utente
const&)'
collect2: ld returned 1 exit status
make: *** [myapp.exe] Error 1
Non riesco a capire perché non riesco a fare l'overloading di << nella
classe Utente!
Grazie

 ostream& operator<<(ostream& os, Utente& u){
Non ho provato a compilare ma può darsi che manchi il const prima di Utente&?
EMG
--
EMAG Software Homepage:     
http://www.emagsoftware.it
The EMG's ZX-Spectrum Page: 
http://www.emagsoftware.it/spectrum
The Best of Spectrum Games: 
http://www.emagsoftware.it/tbosg
The EMG Music page:         
http://www.emagsoftware.it/emgmusic

 
Ehm... *Vergogna* su di me! Era proprio quello.
Già che ci sono: per fare l'overloading di << è sempre necessario
specificare i vari const, sia ai metodi sia ai parametri formali forniti
per ridefinire << ?

 per ridefinire << ?
Non so se ci sia un obbligo specifico dello standard ma in generale basta che
dichiarazione e definizione corrispondano.



Herb Sutter ha descritto una soluzione carina a questo problema in
Exceptional C++. Ti permette di estendere in modo furbo lo streaming
dei dati della classe alle classi derivate.
La soluzione funziona come segue:
/** Persona.h **/
#include <iostream>
class Persona{
  public:
    //si evita di dichiarare l'operatore << come friend
    //si dichiara una funzione print  virtuale e costante
   virtual std::ostream& print( std::ostream& os) const;
  private:
      int codice;
      string indirizzo;
      string cap;
      string comuneResidenza;
      string provinciaResidenza;
      string telefono;
};
inline std::ostream& operator<<( std::ostream& os, const Persona& pers)
{
  return pers.print(os);
}
/** Persona.cpp **/
#include <Persona.h>
std::ostream&  Persona::print( std::ostream& os)const{
  os << codice << "\n";
  os << indirizzo << "\n";
  // altro codice per gli altri dati
  return os;
}
/** Utente.h  **/
#include "Persona.h"
class Utente: public Persona{
  public:
    //override di Persona::print
    virtual std::ostream& print( std::ostream& os) const;
  private:
    // ...
};
/** Utente.cpp **/
#include "Utente.h"
std::ostream&  Utente::print( std::ostream& os )const{
  // streaming dei dati di Utente
  return os;
}
/** Fine codice **/
Non è più necessario fare l'overloading dell'operatore << per
la classe Utente. L'operatore che è stato dichiarato in
Persona.h è più che sufficiente.
In questo modo è possibile avere il supporto per lo streaming
derivando da Persona e facendo l'override della funzione print.
Ciao Ugo

 derivando da Persona e facendo l'override della funzione print.
E' una tecnica pratica, ma non e' detto che sia sempre la soluzione
giusta. Per esempio e' possibile che eseguendo un ciclo su un range di
puntatori a persona ci si aspetti un output uniforme, invece che
variabile a seconda del tipo dinamico di ciascun elemento.
Personalmente tenderei ad implementare un operatore << specifico per
ogni sottoclasse.
Ciao,
Nicola

 OUTPUT uniforme iterando nell'array ?
Se e' un array (di puntatori) e' uniforme per forza, sara' qualcosa
come
std::vector<Persona *> persone;
Se anche ci inserisco puntatori a sottoclassi di persona un operatore
<< implementato senza funzioni virtuali considerera' tutti gli
elementi come istanze di Persona.
Ciao,
Nicola

elementi come istanze di Persona.
Umh... Domanda:
Ma allora non è sufficiente fare uso di operator<< implementato nella base?
Che senso ha implementarlo in maniera personalizzata nelle derivate per poi 
chiamare quello della base?
Il punto è: o si sceglie la personalizzazione dell'output o l'uniformità, 
no?
Ciao 

 no?
Il punto e' proprio lasciare la scelta: se vuoi l'uniformita' usi
sempre puntatori alla base, altrimenti usi puntatori alla classe
effettiva. Con l'implementazione basata su funzioni membro virtuali
c'e' solo un'alternativa.
Ciao,
Nicola

 metodi tramite la Virtual Table. Vero ?
Si chiama "polimorfismo" :-)

c'e' solo un'alternativa.
Ah intendevi in questo senso allora.
Ciao 

 in generale.
Tranquillo, mi sembra che tu abbia capito perfettamente.
Ciao,
Nicola
P.S. Non ti offendere, ma mi sembri la perfetta impersonificazione
dell'alignment chaotic good di D&D :-)

italiana di significato compiuto.
E' una terminologia legata a un famoso gioco: Dungeons&Dragons (D&D).
Da 
http://it.wikipedia.org/wiki/Allineamento
 :
"Caotico Buono (CB): il Ribelle, segue solo la propria coscienza,
senza preccuparsi di ciò che si aspettano gli altri da lui, anche se
ciò comporta l'infrangere una o più leggi che a lui sembrano ingiuste.
Segue la vita del fuorilegge, ma riesce comunque ad essere gentile e
ben disposto. Odia quando qualcuno intimorisce o vuole imporre le
proprie regole agli altri. È il Robin Hood del gioco."
:-)


L'alternativa c'è: si può scegliere se fare una
stampa uniforme, oppure mista.
Ho scritto un piccolo esempio che può servire anche a chiarire
la faccenda delle funzioni virtuali. L'esempio compila così
com'è.
/** Sutter.cpp **/
#include <iostream>
#include <string>
#include <vector>
struct Person{
  virtual std::ostream& print(std::ostream& os) const {
    return os << "I'm a Person\n";
  }
};
inline std::ostream& operator<<(std::ostream& os, const Person& pers){
  return pers.print(os);
}
struct User : public Person{
  virtual std::ostream& print(std::ostream& os) const {
    os << "I'm a User and ";
    return this->Person::print(os);
  }
};
int main(){
  using std::cout;
  using std::vector;
  Person pers1;
  Person pers2;
  User   user1;
  User   user2;
  vector<Person*> ptrPersonVec;
  ptrPersonVec.push_back(&pers1);
  ptrPersonVec.push_back(&pers2);
  ptrPersonVec.push_back(&user1);
  ptrPersonVec.push_back(&user2);
  typedef vector<Person*>::iterator pPersIter;
  pPersIter first( ptrPersonVec.begin() );
  pPersIter last ( ptrPersonVec.end() );
  cout << "Polimorphic calls\n";
  for(first; first != last; ++first){
    cout << **first;
  }
  first = ptrPersonVec.begin();
  last  = ptrPersonVec.end();
  cout << "\nBase class calls\n";
  for(first; first != last; ++first){
    (*first)->Person::print( cout );
  }
  return 0;
}
L'esempio si basa sulla selezione esplicita della funzione
da chiamare:
user1.Person::print(cout);
In ogni caso le soluzioni precotte non sono adatte a tutte le
situazioni. L'importante è riconoscere i pregi e i difetti
della soluzione, come quelli che hai evidenziato, e scegliere in
base alle esigenze.
Con la soluzione di Sutter, su una sequenza di puntatori "misti"
è possibile scegliere se si desidera una stampa uniforme
oppure mista. Se si implementa un'operatore specifico
per ogni classe, non vedo come si possa ottenere una
stampa mista da una sequenza di puntatori "mista".
Si può ottenere solo una stampa uniforme?
Ciao Ugo

 Si può ottenere solo una stampa uniforme?
O effettuando i downcast esplicitamente o implementando il pattern
Visitor.
Ciao,
Nicola

 stampa mista da una sequenza di puntatori "mista".
Ma definendo un operatore per ogni classe il problema mi sembra quello di 
ottenere una stampa uniforme non mista? Nel tuo esempio (di Sutter) accade 
questo e per avere una stampa uniforme devi far uso del metodo print 
specifico della classe base.
Ciao 

 :-)

 segno come descrizione.
Non mi riferivo tanto alle regole del newsgroup, quanto a quelle della
programmazione, dove il tuo approccio appare per l'appunto
"caotico" ;-)
Ciao,
Nicola

 specifico della classe base.
Ho scritto l'esempio per mostrare che non è difficile ottenere una
stampa
uniforme anche quando si è scelto in partenza uno schema per la
stampa mista.
Nell'esempio si vede che non è stato necessario modificare
ne la classe padre ne la classe figlia.
Nicola ha indicato la soluzione per il problema speculare.
L'importante è fare la scelta giusta in base alle esigenze note.
Quando la situazione cambia, l'altenativa è possibile (non
impossibile)
ed ha un "costo" noto.
Ciao, Ugo

 derivando da Persona e facendo l'override della funzione print.
supponendo di voler rimanere generali rispetto al tipo di carattere
dello stream (ansi o unicode), la funzione print deve avere come
template parameter il tipo del carattere (char o wchar_t)
... ma in tal caso non può essere virtuale perchè il C++ non lo
permette
voi come risolvereste?
saluti,
Marco

 derivando da Persona e facendo l'override della funzione print.
supponendo di voler rimanere generali rispetto al tipo di carattere


 derivando da Persona e facendo l'override della funzione print.
supponendo di voler rimanere generali rispetto al tipo di carattere
