Funzioni virtual

di il
73 risposte

73 Risposte - Pagina 5

  • Re: Funzioni virtual

    Ciao eugen,
    Thread vecchio fa buon brodo. Ho provato a compilare il programma e dopo un paio di correzioni marginali (ho solo aggiunto la definizione di alcuni metodi che avevi omesso e commentato la riga graph->m_renderPrimitive();) si compila senza problemi. Per cui non ho capito il problema.

    E poi non ho capito quando parli del metodo m_renderPrimitive come se fosse un puntatore, cosa intendi dire? O meglio ho capito che tu volevi che fosse un puntatore ma lo ha dichiarato come un metodo virtuale, giusto?
  • Re: Funzioni virtual

    Grazie della risposta!
    Hai ragione, ho dimenticato di allegare la definizione di m_renderPrimitive (però nel programma l'ho scritta).

    .CPP
    
    opening::opening(){}  //Constructor 
    opening::~opening(){} //Destructor   
    
    void opening::m_renderPrimitive (void)    //METHOD: renderPrimitive
    {
    	std::cout <<"I'm in renderPrimitive" <<std::endl;	//Qua ci sono definiti in realtà altri metodi
    }
    

    .H:
    class opening: public graphic
    {
    	public:
    		opening();
    		~opening();
    		static void launch();		
    	private:
    		void m_renderPrimitive (void);
    	
    };
    Col senno di poi mi son spiegato malissimo, provo ad essere più chiaro
    m_renderPrimitive() è una funzione che è ridefinita in più classi derivate, vorrei riuscire a fare in modo che *graph mi punti alla classe giusta.
    Ho provato a farlo usando l'operatore new + oggetto della classe a cui puntare, come negli esempi che avevi fatto a suo tempo, ma compilando mi dice, riferito alla riga
    graph->m_renderPrimitive();
    che 'graph is not declared in this scope'.
    Penso che sia perchè l'ho dichiarato nel main. Ma il puntatore *graph non dovrebbe esistere fino alla chiamata di "delete"? E se è effettivamente così, come faccio ad accederci dalla classe "graphic" pur avendolo dichiarato nel main?

    Spero così sia un filo più chiaro, grazie ancora per la disponibilità!
  • Re: Funzioni virtual

    Forse ho capito, stai facendo tutto questo rigiro perché usi void glutDisplayFunc(void (*func)(void)).
    E poi l' errore che dicevi tu è la riga che io ho commentato (pensavo ti riferissi al graph nel main). Beh, quella non è dichiarata.
    Comunque prima di fare confusione aspetto che mi rispondi.

    EDIT ci siamo sovrapposti, mi hai già risposto
  • Re: Funzioni virtual

    Esatto, hai colto il punto. In che senso non è dichiarata? La funzione o il puntatore?
  • Re: Funzioni virtual

    Siccome usi glutDisplayFunc(void (*func)(void)) non le puoi passare un metodo di un oggetto, puoi al limite passare un metodo statico di classe.

    I metodo statici però non hanno il this e possono lavorare solo sui campi statici, per risolvere questo problema nell' esempio ho aggiunto un campo statico m_graph.
    
    #include <iostream>
    using namespace std;
    
    class graphic
    {
       public:
    	   graphic() {}
    	   ~graphic() {};
          static void reshape(int width, int height);
          static void display();
    
       protected:
          virtual void m_renderPrimitive() = 0;
    
    	public: // pubblico solo per ora giusto perchè ho fretta :)
    	  static graphic* m_graph;
    };
    
    class opening: public graphic
    {
       public:
    	   opening() { m_graph = this; }
    	   ~opening() {} 
       private:
    	   void m_renderPrimitive() {std::cout <<"I'm in renderPrimitive" <<std::endl; }
    };
    
    graphic* graphic::m_graph = NULL;
    
    void graphic::display()    
    {
       m_graph->m_renderPrimitive(); 
    }
    
    void MyGlutFunction(void (*func)(void))
    {
    }
    
    int main()
    {
        new opening();
     
       //glutInit(&argc, argv);
       //glutInitDisplayMode (GLUT_SINGLE);
       //glutInitWindowSize (700, 400);
       //glutInitWindowPosition (100, 100);
       //
       //window=glutCreateWindow ("Intro");
       //glutDisplayFunc(graph->display);
       //glutReshapeFunc(graph->reshape);
    
       MyGlutFunction(graphic::m_graph->display);
       
       //glutMainLoop();
    
    	return 0;
    }
    
    Questa si compila, e per ora può andare, comincia a guardarla e ci sentiamo domani, ora devo andare.
  • Re: Funzioni virtual

    Grazie mille barba59!
    Sapevo che glut prende metodi statici (questa è la riscrittura in meglio di codice che avevo fatto), il problema è che non sapevo la sintassi per dichiarare il puntatore nella classe graphic
    Funzionare funziona perfettamente!
    Una di domanda (rispondi quando vuoi, non ti preoccupare!):
    -faccio assumere
    m_graph=this
    nel costruttore, se ad un certo punto nella classe devo farlo riferire a qualche altra classe (esempio, creazione di altra finestra grafica), posso tranquillamente riassegnarlo, no? (non penso ci siano problemi, ma giusto per far chiarezza)

    Continuo a studiarla un po' se mi viene in mente altro chiedo, approfittando della tua gentilezza buona serata!
  • Re: Funzioni virtual

    Ciao eugen,
    molte librerie del C usano delle callback (puntatori a funzione) e quando lavori ad oggetti (sempre) questo diventa una seccatura perchè non puoi assegnare puntatori a metodi. una prima soluzione sarebbe quella di dichiarare una funzione che lavora su un oggetto contenuto in una variable globale:
    
    graphic* globalGraphic = NULL;
    
    void MyCallback()
    {
    	globalGraphic->m_renderPrimitive();
    }
    
    int main()
    {
        globalGraphic = new opening();
        MyGlutFunction(MyCallback);
    
        return 0;
    }
    
    Ovviamente puoi riassegnare globalGraphic ogni volta che ti serve. (questa soluzione ti costringe a dichiarare m_renderPrimitive pubblica)
    Una soluzione più elegante è quella di sostituire la variable globale con una variabile statica di classe e la funzione con un metodo statico (che è quelle che abbiamo fatto nell' esempio di ieri). Anche in questo caso puoi riassegnare la variabile quando vuoi, e non è necessario che sia il costruttore ad assegnarla.

    P.S una nota sulle convenzioni di scrittura che poco riguarda il tuo problema:
    le convenzioni sono tante e varie e ognuno fa quello che preferisce, ma (direi sempre)il prefisso m_ viene usato solo per i campi e non per i metodi, infatti all' inizio m_renderPrimitive mi ha un poco confuso. Se adotti la convenzione dell' m_ poi in genere si adotta l' uso della Pascal convention per i nomi dei metodi e delle classi e la camel convention per i nomi delle variabili.
  • Re: Funzioni virtual

    Chiaro, grazie barba59!
    Un'altra domanda: quando riassegno la variabile con new, devo un delete della vecchia per evitare memory leaks, giusto? (oppure usare uno shared pointer)
    Poi (poi spero di esaurirle!): al posto di
    new opening()
    , perchè non posso fare
    opening o;
    , creare un oggetto di opening (tanto il costruttore assegna this a m_graph) e poi nel main mantenere la stessa chiamata
    MyGlutFunction(graphic::m_graph->display);
    ?

    Va bene, mi adatterò alla convenzione
  • Re: Funzioni virtual

    Si si hai ragione, bisogna fare i delete, questo era solo un esempio.

    Attenzione a fare 'opening o' (senza la new) perchè darebbe un sacco di problemi. Intanto 'o' verrebbe distrutta alla fine dello scope, per cui il puntatore che hai mantenuto (m_graph) punterebbe ad un oggetto distrutto. Poi anche se la mantieni in vita attento a non farne dei delete, altrimenti tenterebbero di distruggere una variablie di stack.
  • Re: Funzioni virtual

    Ok, vediamo allora se ho capito bene.
    Assegno il puntatore all'inizio come abbiamo fatto nell'esempio. Eseguo il codice, arriva il momento di riassegnare il puntatore. Faccio un delete, e lo riassegno, nel seguente modo:
    
    delete m_graph;
    new keyboard();
    glutDisplayFunc(graphic::m_graph->display); 
    
    dove m_graph è il puntatore che c'era prima, e
    new keyboard()
    crea un oggetto della classe keyboard (un'altra classe del programma, fatta come la classe opening dell'esempio precedente, con il costruttore che assegna this al puntatore). A questo punto il puntatore è correttamente riassegnato e il vecchio non dovrebbe più esistere. Giusto?
    Chiedo conferma, il programma gira, mi interessa non compiere errori a livello concettuale
  • Re: Funzioni virtual

    Ciao eugen, tutto giusto.
    Come ti dicevo era un esempio, poi lo puoi sempre migliorare, potresti spostare l' assegnamento di m_graph nel costruttore di Graphic, che si potrebbe occupare anche della distruzione dell' istanza precendente. Ad esempio:
    
    #include <iostream>
    using namespace std;
    
    class Graphic
    {
       public:
    	   Graphic() 
    	   {
    		    Delete();
    		    m_graph = this;
    	   }
    
    	  virtual ~Graphic() {};
              static void Display() { m_graph->RenderPrimitive(); }
    	  
    	  static void Delete() 
    	  { 
    		   if (m_graph)
    		   {
    			   delete m_graph;
    			   m_graph = NULL;
    		   }
    	  }
    
       protected:
           virtual void RenderPrimitive() = 0;
           static Graphic* m_graph;
    };
    
    Graphic* Graphic::m_graph = NULL;
    
    class Opening: public Graphic
    {
       private:
    	   void RenderPrimitive() { std::cout <<"I'm in Opening::RenderPrimitive" <<std::endl; }
    };
    
    class Keyboard: public Graphic
    {
       private:
    	   void RenderPrimitive() { std::cout <<"I'm in Keyboard::RenderPrimitive" << std::endl; }
    };
    
    void MyGlutFunction(void (*func)(void))
    {
    	func();
    }
    
    int main()
    {
        new Opening();
        MyGlutFunction(Graphic::Display);
    
        new Keyboard();
        MyGlutFunction(Graphic::Display);
    
        // e quando non serve più
        Graphic::Delete();
    
        return 0;
    }
    
    N.B. il distruttore di Graphic deve essere virtual, il distruttore deve sempre essere virtual quando usi il polimorfismo, se prendi l' abitudine di farlo sempre virtual non sbagli (salvo casi eccezionali).
  • Re: Funzioni virtual

    La questione della distruzione dell'istanza precedente da parte del costruttore ho bisogno di guardarla bene un attimino.
    Perchè con il polimorfismo il distruttore deve essere virtual? E soprattutto, nelle classi derivate, se queste non hanno metodi virtual (sono stati tutti ridefiniti), il distruttore non deve essere virtual, giusto (solo nella classe base deve esserlo?)?

    Il resto sto cominciando a capirlo, non è un discorso semplicissimo ma applicandomici un po' dovrei riuscire a farci l'abitudine!
  • Re: Funzioni virtual

    M_graph è di tipo Graphic*, e punta a un derivato di Graphic, quando scrivi delete m_graph chiami il distruttore delle classe Graphic ma in realtà ti serve che venga chiamato quello della classe derivata, se non fosse virtuale non verrebbe richiamato quello derivato.

    Quando in una classe scrivi che una funzione è virtuale questa è virtuale per sempre anche in tutte le classi derivate, nelle classi derivate puoi scriverlo o non scriverlo ma rimane virtuale.
  • Re: Funzioni virtual

    Chiaro, grazie mille! Sei riuscito nell'impresa impossibile di farmi capire il polimorfismo
Devi accedere o registrarti per scrivere nel forum
73 risposte