Chiarimento include

di il
3 risposte

Chiarimento include

Salve a tutti. Ho fatto questo semplice programma sull'ereditarietà una gerarchia forma geomentrica-punto-cerchio-cilindro. Il programma funziona ma vorrei dei chiarimenti su come vanno fatti correttamente gli #include. Ad esempio se io ho un interfaccia di una classe con una funzione stampa che usa cout devo includere iostream o no? O lo metto solo nel file .cpp di implementazione. Insomma io vi incollo tutti i moduli del programma, penso che skynet mi potrà chiarire queste cose.

//Classe astratta formageometrica:

#ifndef FORMAGEOMETRICA_H
#define FORMAGEOMETRICA_H

#include <iostream>

using namespace std;

class FormaGeometrica // Classe astratta
{
      public:
             virtual double area() const { return 0.0; }
             virtual double volume() const { return 0.0; }
             
             // Funzioni virtuali pure che subiranno ridefinizioni nelle
             // classi derivate
             
             virtual void StampaNomeFigura() const = 0;
             virtual void StampaParametri() const = 0;
};

#endif
// File Punto.h

#ifndef PUNTO_H
#define PUNTO_H

#include "FormaGeometrica.h"

class Punto : public FormaGeometrica
{
      public:
              Punto (int a = 0, int b = 0); // costruttore di default
              void setPunto (int a, int b);
              int getX() const { return x; }
              int getY() const { return y; }
              virtual void StampaNomeFigura() const { cout << "Punto: "; }
              virtual void StampaParametri() const;
      private:
              int x, y;
              
};

#endif
//File punto.cpp

#include "Punto.h"

Punto::Punto (int a, int b) { setPunto (a, b); }

void Punto::setPunto (int a, int b)
{
     x = a;
     y = b;
}

void Punto::StampaParametri() const
{
     cout << "(" << x << ", " << y << ")";
}
//File Cerchio.h

#ifndef CERCHIO_H
#define CERCHIO_H

#include "Punto.h"

class Cerchio : public Punto
{
      public:
              Cerchio (int x = 0, int y = 0, double r = 0.0);
              void setRaggio (double r);
              double getRaggio() const { return raggio; }
              virtual double area() const;
              virtual void StampaNomeFigura() { cout << "Cerchio: "; }
              virtual void StampaParametri() const;
      private:
              double raggio;
};

#endif
//File Cerchio.cpp

#include "Cerchio.h"

Cerchio::Cerchio (int x, int y, double r) : Punto(x, y) { setRaggio(r); }

void Cerchio::setRaggio (double r)
{
       raggio = r > 0 ? r : 0; // Se il raggio è maggiore di 0 dà r altrimenti 0
}

double Cerchio::area() const
{
       return (3.14159 * raggio * raggio);
}

void Cerchio::StampaParametri() const
{
       Punto::StampaParametri();
       cout << "; Raggio = " << raggio;
}
//File Cilindro.h

#ifndef CILINDRO_H
#define CILINDRO_H

#include "Cerchio.h"

class Cilindro : public Cerchio
{
      public:
              Cilindro (int x = 0, int y = 0, double r = 0.0, double h = 0.0);
              void setAltezza (double h);
              virtual double area() const;
              virtual double volume() const;
              virtual void StampaNomeFigura() const { cout << "Cilindro: "; }
              virtual void StampaParametri() const;
      private:
              double altezza;
};

#endif
// File Cilindro.cpp

#include "Cilindro.h"

Cilindro::Cilindro (int x, int y, double r, double h) : Cerchio (x, y, r)
   { setAltezza(h); }

void Cilindro::setAltezza (double h)
{ 
       altezza = h > 0 ? h : 0; // Se l'altezza è maggiore di 0 dà h altrimento 0
} 
   
/* La formula dell'area di un cilindro è 2*pi*r^2 + 2*pi*r*h: Cerchio::area()
   restituisce pi*r^2 richiamando il metodo area() della classe Cerchio. getRaggio()
   invece mi restituisce il raggio. */
double Cilindro::area() const
{ 
       return (2 * Cerchio::area()) + (2 * 3.14159 * getRaggio() * altezza); 
}
   
// Il volume di un cilindro è pi*r^2*h
double Cilindro::volume() const
{ 
       return Cerchio::area() * altezza; 
}
   
void Cilindro::StampaParametri() const
{
       Cerchio::StampaParametri();
       cout << "; Altezza = " << altezza;
}
//File main.cpp

#include <iomanip>
#include <cstdlib>
#include <iostream>
#include "Cilindro.h"

using namespace std;

void Polimorfismo_puntatore (const FormaGeometrica* Punt); // Passo un punt. alla classe base
void Polimorfismo_riferimento (const FormaGeometrica& Rif);// Passo un rif. alla classe base

int main ()
{
      // Imposto a 2 il numero di cifre da visualizzare dopo il punto decimale
      cout << setiosflags(ios::fixed | ios::showpoint) << setprecision(2);
      
      Punto pun(7, 11); // oggetto punto di coordinate x = 7, y = 11
      Cerchio cer(22, 8, 3.5); // oggetto cerchio con x = 22, y = 8, raggio = 3.5
      Cilindro cil (10, 10, 3.3, 10); // cilindro x = 10, y = 10, raggio = 3.3, altezza = 10
      
      pun.StampaNomeFigura();   // binding statico
      pun.StampaParametri();    // binding statico
      cout << "\n";
      
      cer.StampaNomeFigura();   // binding statico
      cer.StampaParametri();    // binding statico
      cout << "\n";
      
      cil.StampaNomeFigura();   // binding statico
      cil.StampaParametri();    // binding statico
      cout << "\n\n";
      
      // Array di puntatori della classe base inizializzato ai tre oggetti derivati
      FormaGeometrica *ArrayDiFigure[3] = {&pun, &cer, &cil};

      cout << "Binding dinamico attraverso un puntatore" << endl << endl;
      /* Ciclo che attraversa ArrayDiFigure e chiama Polimorfismo_puntatore
         per visualizzare il nome, gli attributi, l'area e il volume di ogni
         oggetto utilizzando il binding dinamico */
      for (int i = 0; i < 3; i++)
         Polimorfismo_puntatore(ArrayDiFigure[i]);
         
      cout << "Binding dinamico attraverso un riferimento" << endl << endl;
      // Stessa cosa precedente usando stavolta un riferimento alla classe base
      for (int i = 0; i < 3; i++)
         Polimorfismo_riferimento(*ArrayDiFigure[i]);
      
      system ("pause");
      return 0;
}

void Polimorfismo_puntatore (const FormaGeometrica* Punt)
{
     Punt->StampaNomeFigura();
     Punt->StampaParametri();
     cout << "\nArea = " << Punt->area()
          << "\nVolume = " << Punt->volume() << "\n\n";
}

void Polimorfismo_riferimento (const FormaGeometrica& Rif)
{
     Rif.StampaNomeFigura();
     Rif.StampaParametri();
     cout << "\nArea = " << Rif.area()
          << "\nVolume = " << Rif.volume() << "\n\n";
} 
Grazie mille. Ripeto vorrei che mi si chiarisse come includere correttamente le librerie soprattutto in queste situazioni di ereditarietà.

3 Risposte

  • Re: Chiarimento include

    Mai mettere "in scope" dei namespace nei file .h
    Se devi utilizzare il cout devi scrivere std::cout.
    Uno che utilizza la tua classe non vedo perche dovrebbe portarsi dietro tutto il namespace std.
    I header guard (come stai faccendo) fanno si che un header venga incluso/compilato una volta sola. L'inclusione dei header a me sembra aposto cos'è che non ti è chiara?
  • Re: Chiarimento include

    Allora cerco di spiegarmi un po' meglio. Nella classe astratta FormaGeometrica ci sono semplicemente funzioni virtuali pure e due funzioni di get. Non è inutile includere il file iostream?

    Il file corretto FormaGeometrica.h è questo?
    
    #ifndef FORMAGEOMETRICA_H
    #define FORMAGEOMETRICA_H
    
    class FormaGeometrica // Classe astratta
    {
          public:
                 virtual double area() const { return 0.0; }
                 virtual double volume() const { return 0.0; }
                 
                 // Funzioni virtuali pure che subiranno ridefinizioni nelle
                 // classi derivate
                 
                 virtual void StampaNomeFigura() const = 0;
                 virtual void StampaParametri() const = 0;
    };
    
    #endif
    
    Passiamo ora al file Punto.h che è una classe derivata da FormaGeometrica. In questo header c'è la funzione virtuale StampaNomeFigura che è definita inline. Questa funzione usa cout per lo stream di output giusto? Quindi in questo file oltre a quello che ho scritto di codice, ossia
    #ifndef PUNTO_H
    #define PUNTO_H

    #include "FormaGeometrica.h"
    ....
    dovrei aggiungere anche
    #include <iostream>
    using std::cout;
    ? Cioè il codice corretto è questo?
    
    #ifndef PUNTO_H
    #define PUNTO_H
    
    #include "FormaGeometrica.h"
    #include <iostream>
    using std::cout;
    
    class Punto : public FormaGeometrica
    {
          public:
                  Punto (int a = 0, int b = 0); // costruttore di default
                  void setPunto (int a, int b);
                  int getX() const { return x; }
                  int getY() const { return y; }
                  virtual void StampaNomeFigura() const { cout << "Punto: "; }
                  virtual void StampaParametri() const;
          private:
                  int x, y;
                  
    };
    
    #endif
    
    Passiamo al file Punto.cpp, l'implementazione. Anche qui uso cout devo scrivere anche qui #include <iostream> using std::cout; ? Il codice corretto è questo?
    
    #include <iostream>
    using std::cout;
    
    #include "Punto.h"
    
    Punto::Punto (int a, int b) { setPunto (a, b); }
    
    void Punto::setPunto (int a, int b)
    {
         x = a;
         y = b;
    }
    
    void Punto::StampaParametri() const
    {
         cout << "(" << x << ", " << y << ")";
    }
    
    A questo punto senza che dica le stesse cose anche per le altre classi incollo il codice che secondo me è corretto. La domanda finale qual è? Bisogna trattare e includere in tutti i file della gerarchia le librerie necessarie a seconda degli oggetti che si usano oppure c'è una qualche forma di ereditarietà anche con le librerie? Cioè se io nella superclasse in questo caso FormaGeometrica.h includo la libreria iostream con using std::cout; questa viene "inclusa" anche nelle classi derivate che ad esempio utilizzano cout? O bisogna come ho ragionato io specificare tutte le librerie necessarie in ogni singolo file che si tratti di un file .h o .cpp? Per esempio se oltre a cout nel file Punto.h c'era anche un manipolatore di stream come setiosflags dovevo scrivere #include <iomanip> using std::setiosflags;? Tratto i file singolarmente o no? Incollo il codice:

    // File punto.cpp
    
    #include <iostream>
    using std::cout;
    
    #include "Punto.h"
    
    Punto::Punto (int a, int b) { setPunto (a, b); }
    
    void Punto::setPunto (int a, int b)
    {
         x = a;
         y = b;
    }
    
    void Punto::StampaParametri() const
    {
         cout << "(" << x << ", " << y << ")";
    }
    
    // File cerchio.h
    
    #ifndef CERCHIO_H
    #define CERCHIO_H
    
    #include "Punto.h"
    #include <iostream>
    using std::cout;
    
    class Cerchio : public Punto
    {
          public:
                  Cerchio (int x = 0, int y = 0, double r = 0.0);
                  void setRaggio (double r);
                  double getRaggio() const { return raggio; }
                  virtual double area() const;
                  virtual void StampaNomeFigura() { cout << "Cerchio: "; }
                  virtual void StampaParametri() const;
          private:
                  double raggio;
    };
    
    #endif
    
    // File cerchio.cpp
    
    #include <iostream>
    using std::cout;
    
    #include "Cerchio.h"
    
    Cerchio::Cerchio (int x, int y, double r) : Punto(x, y) { setRaggio(r); }
    
    void Cerchio::setRaggio (double r)
    {
           raggio = r > 0 ? r : 0; // Se il raggio è maggiore di 0 dà r altrimenti 0
    }
    
    double Cerchio::area() const
    {
           return (3.14159 * raggio * raggio);
    }
    
    void Cerchio::StampaParametri() const
    {
           Punto::StampaParametri();
           cout << "; Raggio = " << raggio;
    }
    
    // File Cilindro.h
    
    #ifndef CILINDRO_H
    #define CILINDRO_H
    
    #include "Cerchio.h"
    #include <iostream>
    using std::cout;
    
    class Cilindro : public Cerchio
    {
          public:
                  Cilindro (int x = 0, int y = 0, double r = 0.0, double h = 0.0);
                  void setAltezza (double h);
                  virtual double area() const;
                  virtual double volume() const;
                  virtual void StampaNomeFigura() const { cout << "Cilindro: "; }
                  virtual void StampaParametri() const;
          private:
                  double altezza;
    };
    
    #endif
    
    // File cilindro.cpp
    
    #include <iostream>
    using std::cout;
    
    #include "Cilindro.h"
    
    Cilindro::Cilindro (int x, int y, double r, double h) : Cerchio (x, y, r)
       { setAltezza(h); }
    
    void Cilindro::setAltezza (double h)
    { 
           altezza = h > 0 ? h : 0; // Se l'altezza è maggiore di 0 dà h altrimento 0
    } 
       
    /* La formula dell'area di un cilindro è 2*pi*r^2 + 2*pi*r*h: Cerchio::area()
       restituisce pi*r^2 richiamando il metodo area() della classe Cerchio. getRaggio()
       invece mi restituisce il raggio. */
    double Cilindro::area() const
    { 
           return (2 * Cerchio::area()) + (2 * 3.14159 * getRaggio() * altezza); 
    }
       
    // Il volume di un cilindro è pi*r^2*h
    double Cilindro::volume() const
    { 
           return Cerchio::area() * altezza; 
    }
       
    void Cilindro::StampaParametri() const
    {
           Cerchio::StampaParametri();
           cout << "; Altezza = " << altezza;
    }
    
    // File main .cpp
    
    #include <iomanip>
    using std::setiosflags;
    using std::setprecision;
    
    #include <cstdlib>
    using std::system;
    
    #include <iostream>
    using std::cout;
    using std::fixed;
    using std::showpoint;
    using std::ios;
    using std::endl;
    
    #include "Cilindro.h"
    
    void Polimorfismo_puntatore (const FormaGeometrica* Punt); // Passo un punt. alla classe base
    void Polimorfismo_riferimento (const FormaGeometrica& Rif);// Passo un rif. alla classe base
    
    int main ()
    {
          // Imposto a 2 il numero di cifre da visualizzare dopo il punto decimale
          cout << setiosflags(ios::fixed | ios::showpoint) << setprecision(2);
          
          Punto pun(7, 11); // oggetto punto di coordinate x = 7, y = 11
          Cerchio cer(22, 8, 3.5); // oggetto cerchio con x = 22, y = 8, raggio = 3.5
          Cilindro cil (10, 10, 3.3, 10); // cilindro x = 10, y = 10, raggio = 3.3, altezza = 10
          
          pun.StampaNomeFigura();   // binding statico
          pun.StampaParametri();    // binding statico
          cout << "\n";
          
          cer.StampaNomeFigura();   // binding statico
          cer.StampaParametri();    // binding statico
          cout << "\n";
          
          cil.StampaNomeFigura();   // binding statico
          cil.StampaParametri();    // binding statico
          cout << "\n\n";
          
          // Array di puntatori della classe base inizializzato ai tre oggetti derivati
          FormaGeometrica *ArrayDiFigure[3] = {&pun, &cer, &cil};
    
          cout << "Binding dinamico attraverso un puntatore" << endl << endl;
          /* Ciclo che attraversa ArrayDiFigure e chiama Polimorfismo_puntatore
             per visualizzare il nome, gli attributi, l'area e il volume di ogni
             oggetto utilizzando il binding dinamico */
          for (int i = 0; i < 3; i++)
             Polimorfismo_puntatore(ArrayDiFigure[i]);
             
          cout << "Binding dinamico attraverso un riferimento" << endl << endl;
          // Stessa cosa precedente usando stavolta un riferimento alla classe base
          for (int i = 0; i < 3; i++)
             Polimorfismo_riferimento(*ArrayDiFigure[i]);
          
          system ("pause");
          return 0;
    }
    
    void Polimorfismo_puntatore (const FormaGeometrica* Punt)
    {
         Punt->StampaNomeFigura();
         Punt->StampaParametri();
         cout << "\nArea = " << Punt->area()
              << "\nVolume = " << Punt->volume() << "\n\n";
    }
    
    void Polimorfismo_riferimento (const FormaGeometrica& Rif)
    {
         Rif.StampaNomeFigura();
         Rif.StampaParametri();
         cout << "\nArea = " << Rif.area()
              << "\nVolume = " << Rif.volume() << "\n\n";
    }            
    
    Ultima domanda. Mi dicesti che nei file che non siano il main meglio usare la forma using std::...
    ma nel main posso usare using namespace std? Che tecnica mi consigli. Comunque con le modifiche che ho scritto il programma parte benissimo. Però chiariscimi i concetti di cui sopra e nel caso postami il tuo codice sugli include nei vari file se non ti chiedo troppo. Grazie
  • Re: Chiarimento include

    Io tendo ad utilizzare nei .h solo i header che mi servono minimalmente. Nes senso se la tua funzione virtual void StampaNomeFigura() const { cout << "Punto: "; } non la dichiari inline, non hai bisogno di iostream nel .h e quindi lo includi solo nel .cpp
    Il concetto giusto è:
    includere nel .h solo i header di cui non pui fare a meno e nel .cpp i restanti.
    Nel main siccome è .cpp includi i restanti che ti servono per il tuo lavoro. Se iostream e incluso in uno dei header che includi nel main, non c'è bisogno di includere iostream di nuovo, ma ciò non guasta perche esistono i header guard che fanno si che un header venga compilato una sola volta.
Devi accedere o registrarti per scrivere nel forum
3 risposte