Pagina 1 di 2 1 2 ultimoultimo
Visualizzazione dei risultati da 1 a 10 su 17

Discussione: [C++] Classi

  1. #1
    Utente di HTML.it L'avatar di ing82
    Registrato dal
    Sep 2014
    Messaggi
    177

    [C++] Classi

    Il problema è il seguente:

    mi serve creare un oggetto che mi permetta di gestire la sezione e relative caratteristiche di elementi.

    Per iniziare diciamo che avrò a che fare con tre tipi di sezioni, che poi aumenteranno: sezione rettangolare, circolare, generica.

    Durante l'esecuzione del programma, l'utente potrebbe decidere di variare la sezione, passando ad esempio da sezione rettangolare a sezione circolare.

    Pensavo di gestire la cosa così:

    1) Creo una classe per ciascun tipo di sezione
    codice:
    class Sezione_rettangolare_type
    {
    public:
    Sezione_rettangolare_type();
    ~Sezione_rettangolare_type();
    void calcola_area();
    void calcola_perimetro();
    protected:
    double area;
    double perimetro;
    ......
    }
    codice:
    class Sezione_circolare_type
    {
    public:
    Sezione_circolare_type();
    ~Sezione_circolare_type();
    void calcola_area();
    void calcola_perimetro();
    protected:
    double area;
    double perimetro;
    ......
    }
    codice:
    class Sezione_generica_type
    {
    public:
    Sezione_generica_type();
    ~Sezione_generica_type();
    void calcola_area();
    void calcola_perimetro();
    protected:
    double area;
    double perimetro;
    ......
    }
    2) Creo una classe che mi permetta di gestire i diversi tipi di sezione

    codice:
    class Sezione_type
    {
    public:
    Sezione_type(int tipo_sez);
    ~Sezione_type();
    void cambia_sezione(int tipo_vecchio, int tipo_nuovo);
    protected:
    Sezione_generica_type *sezgenerica;
    Sezione_rettangolare_type *sezrettangolare;
    Sezione_circolare_type *sezcircolare;
    int tipo_sezione;
    }
    Può funzionare, oppure un caso del genere è da gestire in modo diverso?

    Grazie.

  2. #2
    Utente di HTML.it L'avatar di ing82
    Registrato dal
    Sep 2014
    Messaggi
    177
    Approfondendo la questione (=studiare), mi viene in mente che la mia situazione possa essere gestita in maniera forse più efficiente nel seguente modo (a parte il fatto di aver implementato le funzioni membro direttamente all'interno della classe, ma non ho a disposizione il codice che ho testato con la classica divisione nei file .h e . cpp)

    1)
    codice:
    class Sezione_type
    {
    public:
    Sezione_type()
    {
    area=0;
    };
    ~Sezione_type();
    virtual void calcola_area();
    ...
    protected:
    double area;
    };
    2)
    codice:
    class Sezione_rettangolare_type
    {
    public:
    Sezione_rettangolare_type(double B, double H):Sezione_type()
    {
    base=B;
    altezza=H;
    calcola_area();
    };
    ~Sezione_rettangolare_type();
    void calcola_area()
    {
    area=base*altezza;
    };
    protected:
    double base;
    double altezza;
    };
    3)
    codice:
    class Sezione_circolare_type
    {
    public:
    Sezione_circolare_type(double D):Sezione_type()
    {
    diametro=D;
    calcola_area();
    }
    void calcola_area()
    {
    area=3.14*pow(D,2.0)/4.0;
    }
    protected:
    double diametro;
    };
    4)in questo modo, nel programma posso fare
    codice:
    int main()
    {
    Sezione_type *sezione;
    ...
    //righe di codice che permettono di scegliere se sezione rettangolare o circolare e fare l'input dei dati come base e
    //altezza o in alternativa diametro
    ...
    //in base a quanto fatto prima se ho scelto sezione rettangolare
    sezione = new Sezione_rettangolare_type(B,H);
    //se ho scelto la sezione circolare
    sezione = new Sezione_circolare_type(D);
    ....
    //righe di codice che permettono di scegliere una sezione di forma diversa e relativo input dati
    //quindi distruggo la sezione precedentemente creata
    delete sezione;
    ...
    //in base al nuovo tipo di sezione scelta, rifaccio
    sezione = new ...//e qui ci metto il corrispondente nuovo tipo di costruttore
    ...
    //fine del programma
    delete sezione;
    return 0;
    }
    Ora pongo le seguenti domande:
    1) se al posto di
    codice:
    virtual void calcola_area();
    avessi fatto
    codice:
    virtual void calcola_area()=0;
    nella classe "Sezione_type" tutto quanto riportato nella precedente parte di ipotetico main non avrei potuto
    farlo, perdendone i vantaggi, ho capito giusto? (però se è stato inventato in qualche caso tornerà utile, ma per ora non riesco a capire quando)
    2) se in "Sezione_type" avessi semplicemente scritto
    codice:
    void calcola_area();
    e nelle altre classi avessi reimplemento la funzione membro (dovrebbe essere overload, spero di non sbagliarmi), avrei anche in questo caso perso la possibilità di definire all'inizio un oggetto di tipo "generale" come Sezione_type, e "particolareggiarlo" successivamente
    durante l'esecuzione del programma, corretto?

    Grazie

  3. #3
    Utente di HTML.it L'avatar di shodan
    Registrato dal
    Jun 2001
    Messaggi
    2,381
    1)
    codice:
    virtual void calcola_area();
    In questo caso devi fornire un'implementazione del metodo in sezione_type, in quanto potresti usare sezione_type come classe a se stante.
    codice:
    virtual void calcola_area()=0;
    In questo caso sei obbligato a dare un'implementazione della funzione nella classe derivata, in quanto il metodo è astratto.
    2)
    Si, in quel modo verrebbe richiamato sempre il metodo della classe base.
    Ricapitolando:
    codice:
    void calcola_area(); // richiama sempre metodo classe base a prescindere dalle derivate (static binding)
    virtual void calcola_area(); // richiama metodo classe derivata solo se presente. Se no metodo classe base (dynamic binding)
    virtual void calcola_area()=0; // occorre definire il metodo nelle classi derivate, altrimenti non compila. (dynamic binding)
    3) Le classi basi devono avere il distruttore dichiarato virtual, altrimenti il distruttore delle derivate non sarà invocato.
    Ultima modifica di shodan; 22-09-2015 a 13:20
    This code and information is provided "as is" without warranty of any kind, either expressed
    or implied, including but not limited to the implied warranties of merchantability and/or
    fitness for a particular purpose.

  4. #4
    Utente di HTML.it L'avatar di ing82
    Registrato dal
    Sep 2014
    Messaggi
    177
    Ok, grazie.
    Chiariti questi aspetti, mi torna il dubbio iniziale: per gestire diversi tipi di sezione che possono cambiare durante l'esecuzione, quale soluzione è migliore? Quella prospettata nel primo post, oppure la seconda?
    Oppure sono valide entrambe e in base all'esperienza e soprattutto a quello che è lo scopo del programma, è da scegliere una o l'altra?

  5. #5
    Utente di HTML.it L'avatar di shodan
    Registrato dal
    Jun 2001
    Messaggi
    2,381
    Il tuo codice (riportato qui) non compila nemmeno. Solo ora mi sono accorto che non esiste ereditarietà tra le classi, il rende la discussione falsata in partenza (hai riportato male il codice?)

    In caso di ereditarietà, molto meglio la seconda soluzione (pattern Strategy http://www.vincehuston.org/dp/strategy.html)

    Ancora meglio usando smart pointer (qui uso std::unique_ptr).

    Notare che quello che viene fatto nel main può essere a sua volta incapsulato in una classe secondo il pattern State:
    http://www.vincehuston.org/dp/state.html

    codice:
    int main()
    {
    
    std::unique_ptr<Sezione_type> sezione;
    ...
    //righe di codice che permettono di scegliere se sezione rettangolare o circolare e fare l'input dei dati come base e
    //altezza o in alternativa diametro
    ...
    //in base a quanto fatto prima se ho scelto sezione rettangolare
    sezione.reset(new Sezione_rettangolare_type(B,H));
    sezione->calcola_area();
    
    //in base al nuovo tipo di sezione scelta, rifaccio
    sezione.reset(new Sezione_circolare_type(D));
    sezione->calcola_area();
    
    return 0;
    }
    This code and information is provided "as is" without warranty of any kind, either expressed
    or implied, including but not limited to the implied warranties of merchantability and/or
    fitness for a particular purpose.

  6. #6
    Utente di HTML.it L'avatar di ing82
    Registrato dal
    Sep 2014
    Messaggi
    177
    Quote Originariamente inviata da shodan Visualizza il messaggio
    Il tuo codice (riportato qui) non compila nemmeno. Solo ora mi sono accorto che non esiste ereditarietà tra le classi, il rende la discussione falsata in partenza (hai riportato male il codice?)
    Per quanto riguarda il codice del primo mio intervento, non lo ho mai provato, per quanto riguarda il secondo, avevo accennato qualcosa
    Quote Originariamente inviata da ing82 Visualizza il messaggio
    Approfondendo la questione (=studiare), mi viene in mente che la mia situazione possa essere gestita in maniera forse più efficiente nel seguente modo (a parte il fatto di aver implementato le funzioni membro direttamente all'interno della classe, ma non ho a disposizione il codice che ho testato con la classica divisione nei file .h e . cpp)
    Grazie
    Sono andato a memoria di quanto avevo fatto...molto probabile che ci sia qualche strafalcione, però non capisco perchè dici
    Quote Originariamente inviata da shodan Visualizza il messaggio
    non esiste ereditarietà tra le classi
    ...oooops

    adesso, rileggendo meglio, direi proprio di si, dopo Sezione_rettangolare_type e Sezione_circolare_type ci andava un bel
    codice:
    class Sezione_rettangolare_type:public Sezione_type
    ...
    
    class Sezione_cicolare_type:public Sezione_type
    ...
    Per il resto, vado ad approfondire.

    Grazie

  7. #7
    Utente di HTML.it L'avatar di ing82
    Registrato dal
    Sep 2014
    Messaggi
    177
    Quote Originariamente inviata da shodan Visualizza il messaggio
    Ancora meglio usando smart pointer (qui uso std::unique_ptr).
    Ma quindi tendenzialmente, se ho dati membro di una classe dichiarati come puntatori, conviene definirli come smart pointer, così evito nel distruttore di dover richiamare il delete, corretto?

    codice:
    class Classe
    {
      public:
        //costruttore, ecc;
        //il distruttore è
        ~Classe(){delete(puntatore);/*altre eventuali cose*/};
      private:
        //quello che serve
        tipoDelPuntatore* puntatore;
    }
    diventa
    codice:
    class Classe
    {
      public:
        //costruttore, ecc;
        //il distruttore è
        ~Classe(){/*altre eventuali cose, ma delete(puntatore) non dovrebbe servire più*/};
      private:
        //quello che serve
        std::unique_ptr<tipoDelPuntatore> puntatore;
    }
    Ne approfitto per una ulteriore domanda: se il dato membro di tipo puntatore sarà usato solo "interno" alla classe, uso unique_ptr, se invece sarà impiegato anche da qualche altro oggetto, uso shared_ptr, corretto?

    Grazie, grazie, grazie

  8. #8
    Utente di HTML.it L'avatar di shodan
    Registrato dal
    Jun 2001
    Messaggi
    2,381
    Quote Originariamente inviata da ing82 Visualizza il messaggio
    Ma quindi tendenzialmente, se ho dati membro di una classe dichiarati come puntatori, conviene definirli come smart pointer, così evito nel distruttore di dover richiamare il delete, corretto?
    Tendenzialmente si, ma non c'è una regola fissa: occorre valutare quello che si vuole fare.
    Lo shared_ptr mantiene un contatore interno di quante volte è stato copiato e l'ultimo a uscire cancella il puntatore, MA la memoria condivisa è la stessa. Se devi fare una deep copy dell'oggetto puntato lo devi fare esplicitamente.

    codice:
        shared_ptr<int> p = new int(10); // contatore = 1
        shared_ptr<int> q = p; // contatore = 2
        *q = 20; 
        cout << *p << endl; // 20 perché il puntatore interno è condiviso tra p e q.
    
        shared_ptr<int> z = new int;
        *z = *q;
        *q = 30;
    
        cout << *z << endl; // 20 perché il puntatore interno di z non è condiviso con p e q.
        cout << *p << endl; // 30 perché il puntatore interno è condiviso tra p e q.
    Con unique_ptr la faccenda è molto più semplice: non può essere copiato.
    codice:
        unique_ptr<int> p(new int(10))
        unique_ptr<int> q = p; // errore: unique_ptr non può essere copiato
    
        unique_ptr<int> q = std:move(p); // ma può essere spostato.
        // cout << *p << endl;  crash: il puntatore interno di p ora è nullptr
        cout << *q << endl; // vale 10.
    
        unique_ptr<int> z (new int);
        *z = *q;
    Ne approfitto per una ulteriore domanda: se il dato membro di tipo puntatore sarà usato solo "interno" alla classe, uso unique_ptr, se invece sarà impiegato anche da qualche altro oggetto, uso shared_ptr, corretto?
    Non proprio: userari shared_ptr se la tua classe dev'essere copiabile, quindi condividere il dato interno con copie di classe uguale; userai unique_ptr se non vuoi che la tua classe sia copiabile e quindi non condividere il dato interno con copie di classe uguale.
    Ambedue i puntatori possono essere restituiti da funzioni, ma se restituisci uno shared_ptr avrai il dato interno esistente, se usi uno unique_ptr stai dicendo: "classe X non devi più occuparti della gestione di unique_ptr<dato> perché ci pensa qualcun altro.

    Spero di essermi spiegato.
    This code and information is provided "as is" without warranty of any kind, either expressed
    or implied, including but not limited to the implied warranties of merchantability and/or
    fitness for a particular purpose.

  9. #9
    Utente di HTML.it L'avatar di ing82
    Registrato dal
    Sep 2014
    Messaggi
    177
    Quote Originariamente inviata da shodan Visualizza il messaggio
    Spero di essermi spiegato.
    Direi di sì. Il mio problema è la mancanza di esperienza, quindi dovrò ora fare qualche prova e capire con la pratica.
    Così a naso, direi che potrei partire usando unique_ptr (sempre riferito a dati membro di una classe) e quando mi accorgo che in realtà avrei fatto meglio ad usare shared_ptr, cambiare.

    Per ora grazie della disponibilità, ora faccio qualche prova.

  10. #10
    Utente di HTML.it L'avatar di ing82
    Registrato dal
    Sep 2014
    Messaggi
    177
    Prova n° 1

    codice:
    #include <iostream>
    #include <stdlib.h>
    #include <memory>
    
    using namespace std;
    
      class Prova
      {
        public:
          Prova(){cout<<"\nCreato l'oggetto Prova!";};
          ~Prova(){cout<<"\nDistrutto l'oggetto Prova!";};
          void run(){cout<<"\nProva is running!";};
          void pause(){cout<<"\nProva is paused!";};
          void termin(){cout<<"\nProva is terminated!";};
      };
    
      class Oggetto
      {
        public:
          Oggetto(){cout<<"\nCreato l'oggetto Oggetto!";};
          Oggetto(shared_ptr<Prova> puntatore):mProva(puntatore){cout<<"\nCreato l'oggetto Oggetto!";};
          ~Oggetto(){cout<<"\nDistrutto l'oggetto Oggetto!";};
          void run(){cout<<"\nOggetto is running!";mProva->run();};
          void pause(){cout<<"\nOggetto is paused!";mProva->pause();};
          void termin(){cout<<"\nOggetto is terminated!";mProva->termin();};
        private:
          shared_ptr<Prova> mProva;
      };
    
    int main()
    {
      {
        unique_ptr<Prova> prova(new Prova);
        prova->run();
        prova->pause();
        prova->run();
        prova->termin();
      }
      system("pause");
      return 0;
    }
    Al termine dell'esecuzione vedo che viene richiamato il distruttore di prova.
    Ultima modifica di ing82; 23-12-2015 a 23:31

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2025 vBulletin Solutions, Inc. All rights reserved.