2013-04-03 18 views
6

Ho due classi, con due friend oparator<< s globali.Operatori globali e polimorfismo

class A { 
    friend std::ostream& operator<<(std::ostream& o, const A &a); 
}; 

class B: public A { 
    friend std::ostream& operator<<(std::ostream& o, const B &b); 
}; 

se lo uso come questo, tutto funziona bene, la versione B dell'operatore viene chiamato:

B b; 
std::cout << b; 

Ma se io uso polymorpism, la versione A viene chiamato, anche se la dinamica tipo è B:

A* b = new B(); 
std::cout << *b; 

una soluzione è colata:

std::cout << static_cast<B&>(*b); 

ma c'è una soluzione più semplice o più elegante per questo?

+2

E il richiamo di una funzione virtuale in 'operator << (std :: ostream &, A const &)'? – dyp

risposta

10

Sì. Un operatore di output e la funzione virtual print nelle classi.

class A 
{ 
public: 
    virtual ~A() {} 
private: 
    virtual void print(std::ostream&) {} 
    friend std::ostream& operator << (std::ostream& os, const A& obj) 
    { 
     obj.print(os); 
     return os; 
    } 
}; 

class B 
{ 
private: 
    virtual void print(std::ostream&) {} 
}; 

Live example

+0

per favore aggiungi il 'return os;' alla funzione friend. – scones

+1

per favore rimuovi il 'amico' da quella funzione - non è necessario essere un amico ma può essere una funzione gratuita;) –

+0

@ArneMertz la stampa è privata. In che modo l'operatore << può essere libero in questo caso? E perché abbiamo bisogno della stampa in pubblico? – ForEveR

3

versioni di funzioni in classi derivate vengono chiamati solo quando vi si accede tramite un puntatore alla classe base se li si definisce come virtual perché il compilatore non ha la minima idea di ciò che la classe di l'oggetto puntato dal puntatore in realtà lo è.

Qui il problema è che si sta definendo funzioni friend in modo che non possano essi stessi essere virtuale, la soluzione è semplice: hanno l'attuazione di un operator<< chiamata di una funzione virtuale in A che si può quindi sovraccaricare in B.

2

Semplicemente non usare gli amici. Sono un accoppiamento ancora più stretto rispetto all'ereditarietà. Soprattutto se scrivi quegli operatori di amici per i modelli di classe, potresti specializzarli e ottenere legalmente accesso agli interni della tua classe. Per questi motivi, uso questi operatori solo come zucchero sintattico e li autorizzo a delegare funzioni membro che svolgono il vero lavoro. In questo modo la soluzione al vostro problema è un gioco da ragazzi:

class A { 
public: 
    virtual std::ostream& printToStream(std::ostream& os) const; 
}; 

std::ostream& operator<<(std::ostream& os, A const& a) 
{ return a.printToStream(os); } 

class B: public A { 
    virtual std::ostream& printToStream(std::ostream& os) const; 
}; 

avere un'altra classe C derivato da A? Nessun problema, non c'è bisogno di definire lo zucchero sintattico (vale a dire operator<<), basta definire come viene svolto il vero lavoro, cioè sostituire printToStream - questo è il motivo per cui l'ho reso virtuale.