Newsgroup: it.comp.lang.c++
Titolo: classi virtuali senza dati membro

Avrei bisogno di implementare un codice tipo il seguente:
struct Mode {
   int (*f)(int x);
   int (*g)(int x);
};
class A {
   private:
     int x;
     Mode *mode;
   public:
     int f() {return mode->f(x);};
     int g() {return mode->g(x);};
};
sostanzialmente la classe A reagisce ai comandi f() e g() a seconda 
della modalita' mode. Scritta in questo stile C, risulta un po' noioso 
dover mantenere e inizializzare degli oggetti statici mode1, mode2, 
mode3 da cui prendere i puntatori da memorizzare in A.mode, ma 
l'implementazione e' molto efficiente.
Mi sembra che in C++ dovrei sfruttare le funzioni virtuali, ad esempio:
class Mode {
   public:
   virtual int f(int x)=0;
   virtual int g(int x)=0;
};
class Mode1: public Mode {
   // implemento f e g
};
class Mode2: public Mode {
   // implemento f e g
};
ora pero' mi ritrovo le classi Mode1 e Mode2 che non hanno dati membro, 
la classe conterra' solamente il puntatore alla tabella virtuale. Dovrei 
poi creare un rappresentante di ogni classe
Mode1 mode1;
Mode2 mode2;
per poterne poi prendere il puntatore da memorizzare in A.mode.
Concettualmente, pero', mi sembra stupido dover creare gli oggetti 
mode1, mode2. Infatti tutti gli oggetti di tipo Mode1 sono uguali tra 
loro (perche' non hanno dati membro che li possano distinguere). Mi 
sembra che con questa implementazione C++ ci sia una indirezione di 
troppo rispetto a quella in C. Infatti in A.mode potrei memorizzare 
direttamente un puntatore alla tabella virtuale della classe Mode1 o 
Mode2, invece mi ritrovo a memorizzare un puntatore al puntatore alla 
tabella virtuale.
Non so se mi sono spiegato... come risolverste voi il problema?
E.

};
Forse non ho capito bene il problema, ma cosě puň andare ?:
#include <iostream>
class CBase {
protected:
   int x;
public:
   virtual int f() = 0;
   virtual int g() = 0;  
   CBase(const int value=0): x(value) {};
};
class CDerived1: public CBase {
public:
   int f() { return x*2; }
   int g() { return x/2; }   
   CDerived1(const int value=0): CBase(value) {};
};
class CDerived2: public CBase {
public:
   int f() { return x/2; }
   int g() { return x*2; }      
   CDerived2(const int value=0): CBase(value) {};   
};
int main(int argc, char *argv[])
{
    CBase *p1 = new CDerived1(10);
    CBase *p2 = new CDerived2(10);
 
    std::cout << "p1->f() = " << p1->f() << std::endl;
    std::cout << "p1->g() = " << p1->g() << std::endl;
 
    std::cout << "p2->f() = " << p2->f() << std::endl;
    std::cout << "p2->g() = " << p2->g() << std::endl;        
    return 0;
}

 Forse non ho capito bene il problema, ma cosě puň andare ?:
Beh, in effetti non ho detto una cosa fondamentale, ovvero che la 
variabile "mode" viene settata solo dopo che la classe A e' stata 
creata. Quindi non posso fare due classi A1 e A2 con funzioni virtuali f 
e g. Il modo "giusto" dovrebbe essere quello di fare due classi Mode1 e 
Mode2, ma in questo caso si incorre nell'inefficienza della doppia 
ridirezione oltre che al problema di dover allocare dinamicamente la 
classe.
E.

 classe.
Siamo realisti: quanti milioni di volte devi chiamare in totale f() o
g()? Perche' se la risposta e' meno di uno, in termini di prestazioni
potrebbe non esserci alcuna differenza concretamente rilevabile. Se
poi definisci A::f() e A::g() inline c'e' una buona probabilita' che
il compilatore elimini una delle due indirezioni.
Ciao,
Nicola

ridirezione 
Che vuoi dire?

tabella virtuale.
Mi riallaccio qui, ho capito che vuoi dire.
Qual è l'implementazione C alla quale ti riferisci ? 

 Che vuoi dire?
Quando chiami a1.f bisogna chiamare la funzione a1.m_pMode->f e siccome 
la funzione e' virtuale c'e' una ulteriore indirezione per trovare 
l'indirizzo della funzione nella virtual table. Nella prima 
implementazione che ho mostrato (quella in stile C), invece c'e` una 
sola indirezione.
E.


Ho detto C, ma in realta' e' sempre C++, ma si sta sostanzialmente 
simulando una chiamata virtuale come si farebbe in C, utilizzando dei 
puntatori a funzione:
struct Mode {
   int (*f)(int x);
   int (*g)(int x);
};
class A {
   private:
     int x;
     Mode *mode;
   public:
     int f() {return mode->f(x);};
     int g() {return mode->g(x);};
};
E.

Ho meditato un po' sulla questione. In effetti quello che io vorrei 
memorizzare nella variabile "mode" della classe A, e' un puntatore alla 
tabella virtuale di una classe derivata "Mode1" da "Mode". Se io potessi 
prendere tale puntatore potrei (in linea di principio) chiamare le 
funzioni virtuali di Mode1 senza avere nessun oggetto della classe 
Mode1. Questo perche' comunque tali funzioni sono essenzialmente 
statiche in quanto l'oggetto Mode1 non ha dati membro.
In effetti cercando "static virtual" su internet ho trovato delle cose 
interessanti. Da quello che ho capito non esiste lo "static virtual" in 
C++, ma in effetti avrebbe senso mettercelo, coerentemente con la 
gestione dinamica dei tipi. Uno potrebbe accedere alle funzioni "static 
virtual" utilizzando il tipo di una classe, senza avere nessun oggetto 
di quella classe.
E.

 di quella classe.
Avrai quindi anche letto l'obiezione standard: senza un oggetto di una
specifica sottoclasse di mode, come fai a individuare il tipo dinamico
che ti interessa?
Ciao,
Nicola

di quella classe.
Hummm... ma come si potrebbe innescare la gestione dinamica di un
tipo, senza avere un oggetto che fa un override dei metodi ?

 che ti interessa?
Con una variabile che ha memorizzato il tipo. In una ipotetica gestione 
dinamica dei tipi sarebbe possibile avere una variabile il cui valore e' 
il tipo di una classe. Tramite questa variabile sarebbe possibile 
accedere ai membri static della classe.
E.