Newsgroup: it.comp.lang.c++
Titolo: ereditarietà e operatori

ho ancora un problema legato agli operatori. ho definito una classe che
contiene due valori, con vari operatori = + *
class Conteggio : public std::pair<float,float>
{
 public:
    float& count;
    float& err;
    Conteggio(): std::pair<float,float>(0.,0.), count(first), err(second)
{};
    Conteggio(float count, float err): std::pair<float,float>(count,err),
count(first), err(second) {};
    inline void scala(const float f) { count *= f;  err *= f; };
    Conteggio& operator*(const float f) { return *(new Conteggio(count * f,
err *f)); };
    Conteggio& operator+(const Conteggio& altro) 
        {
            return *(new Conteggio(count + altro.count, sqrt(err*err +
altro.err*altro.err)));
        };
    Conteggio& operator=(const Conteggio& altro)
        {
            if (this==&altro) return *this;
            first = altro.first;
            second = altro.second;
            return *this;
        }
};
***ora ne eredito un'altra:
class Cristallo : public Conteggio, public Geobj
{
 public:
    Cristallo();
};
std::ostream& operator<<(std::ostream& os, const Cristallo& s);
la classe Geobj non fa quasi nulla per ora. L'implementazione:
Cristallo::Cristallo()
    :Conteggio(), Geobj(LARGHEZZACRISTALLO,
                        LARGHEZZACRISTALLO,
                        LARGHEZZACRISTALLO) {}
std::ostream& operator<<(std::ostream& os, const Cristallo& s)
{
    os << "Count: " << s.count << " +- " << s.err;
    return os;
}
***e il main:
int main(int argc, char **argv)
{
    Cristallo c;
    c.count = 3;
    c.err = 1;
    Cristallo d;
    d = c * 3;
    return 0;
}
e mi dà errore (gcc 4.1.0):
error: no match for ‘operator=’ in ‘d =
c.Cristallo::<anonymous>.Conteggio::operator*(3.0e+0f)’
note: candidates are: Cristallo& Cristallo::operator=(const Cristallo&)
proprio non capisco. Se dichiaro le variabili di tipo Conteggio tutto fila
liscio e funziona.

 c.Cristallo::<anonymous>.Conteggio::operator*(3.0e+0f)?
'operator*' restituisce un 'Conteggio'. Non c'è un 'operator=' disponibile
che accetti un Conteggio, in quanto quello ereditato dalla classe base
Conteggio è *nascosto* dall'operator= implicitamente definito (dal
compilatore) per Cristallo.
Aggiungi 'using Conteggio::operator=' nella definizione di Cristallo, se
vuoi che quell'operator= sia considerato un candidato diponibile per
risoluzione dell'overload.
 
Max

 (Stesso discorso per operator+).
hai ragione, però a me hanno sempre insegnato che è meglio non ritornare gli
oggetti per valore per questioni di ottimizzazione.

 gli oggetti per valore per questioni di ottimizzazione.
Innanzitutto, l'ottimizzazione è subordinata ad altre esigenze più
importanti, tra cui il controllo del lifetime degli oggetti, che è
automatico se si usano oggetti -appunto- automatici ed è "manuale" se si
usano oggetti con lifetime dinamico.
In secondo luogo, non è affatto detto, nel caso in questione, che restituire
un nuovo oggetto per valore (con lifetime automatico) sia meno efficiente
della tua soluzione (che comunque non è un'opzione possibile, a meno che tu
non voglia perdere risorse ad ogni chiamata della funzione).
Anzi, date le ottimizzazioni possibili dallo Standard (e implementate da
tutti i compilatori recenti), la mia implementazione molto probabilmente
istanzierà un *singolo* oggetto temporaneo automatico (che è un'operazione
più efficiente rispetto a instanziare con 'new'). Puoi verificarlo
facilmente tracciando le chiamate di costruttori e distruttori.
Max

 facilmente tracciando le chiamate di costruttori e distruttori.
Senza contare che anche senza queste ottimizzazioni l'allocazione di
memoria comporta una chiamata a funzione ed il relativo context switch
probabilmente costa di piu' della copia, anche doppia, di una
struttura di dimensioni piuttosto contenute, senza parlare del costo
dell'algoritmo di allocazione.
Ciao,
Nicola

wiso ha scritto:

può essere vero, ma per non creare memory leaks (come 
allocazioni e deallocazioni spaiate), a quel punto imho conviene 
modificare la sintassi e passare le varie reference, inclusi i 
valori restituiti di output, dentro i parametri ricevuti.
Poi magari puoi anche duplicare la reference ricevuta dal 
chiamante nel parametro "di output" nel valore di ritorno, per 
consentire una sintassi agile delle chiamate di funzione, anche 
a cascata (come a = b = c). Ma in quel modo sottolinei che il 
tuo operatore non esegue alcuna operazione di gestione memoria e 
delega ogni cosa al chiamante.
Inoltre, seppure in parte il principio che hai citato è giusto, 
considera che non sempre è vero. Ad es., se gli oggetti passati 
dentro e fuori sono abbastanza piccoli, la velocità di creazione 
/distruzione di oggetti automatici sullo stack rende esiguo il 
vantaggio del passaggio di reference. Quindi non è sempre vero 
che il guadagno sia molto rilevante nemmeno quando tu passi solo 
reference, e molto di rado è vero quando oltre a usare reference 
gestisci la memoria dinamica (heap), dato che lo stack sovrasta 
la velocità di allocazione con new/delete sullo heap (lo stack è 
ideale per oggetti piccoli e numerosi, frammentati).
ciao
Soviet_Mario

> Max M. wrote:
scusate, non avevo fatto il refresh dei threads. Mi accordo ora 
che praticamente quello che ho scritto era inutile, dato che Max 
M. e Nicola Musatti avevano già risposto prima di me.
Sorry
Soviet_Mario


 Max
ti credo. ma allora la funzione della stl char* string::c_str() non si
preoccupa di deallocare la memoria puntata dal puntatore di ritorno?

 preoccupa di deallocare la memoria puntata dal puntatore di ritorno?
No, perché, se non vado errato, c_str() restituisce il puntatore alla 
rappresentazione interna della stringa che, ovviamente, non deve essere 
deallocata dall'esterno. Inoltre mi pare che restituisca un const char *.
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
 

 deallocata dall'esterno. Inoltre mi pare che restituisca un const char *.
Non e' obbligatorio, ma potrebbe farlo. Il buffer e' sotto il
controllo della libreria e la sua validita' e' limitata.
Ciao,
Nicola

 preoccupa di deallocare la memoria puntata dal puntatore di ritorno?
Certo che se ne preoccupa. Infatti il codice client non deve occuparsene.
Il periodo di validità di quel puntatore è rigorosamente definito dallo
Standard ed è controllato unicamente dall'oggetto string. Il codice client
non può considerare valido quel puntatore dopo una qualsiasi successiva
chiamata a una funzione membro non const.
Però questo non c'entra molto con il discorso che stavo facendo. Il succo di
quello che stavo dicendo è: se allochi con 'new', devi assegnare a qualcuno
(e anche eventualmente spostare) la responsabilità di chiamare 'delete' (e
di chiamarla una ed una sola volta). Se non lo fai, prima o poi avrai roba
in giro che nessuno potrà rilasciare e nessuno potrà nemmeno usare (=leak).
Max

 reference):
ultima cosa (spero), ma se faccio così:
Conteggio operator*(const float f) 
    {
      Conteggio a(count*f,err*f);
      return a;
    };
non ho problemi di life time?

 non ho problemi di life time?
ho detto una scemenza, scusate.