Problema c++ con OOP

di il
12 risposte

Problema c++ con OOP

Buonasera a tutti, sto imparando la OOP in C++, in maniera semplifcata, studiandola da un libro di testo per scuole superiori. Sono arrivato al Polimorfismo, e qui ci sto capendo poco.

Vi allego il testo e il codice di un esercizio che non riesco a compilare, in modo da poter capire cosa sbaglio:

Una classe Solido consente di rappresentare una figura solida e di calcolarne il volume;
derivare da essa la classe Bilancia che consente di modificare il metodo per il calcolo, in
modo da ottenere anche il peso del solido conoscendone il peso specifico (peso = volume
x peso specifico).

#include <iostream>
#include <cmath>
using namespace std;
class solido{
    protected:
    float spigolo;
    public:
    solido();
    void assegna(float);
    float calcola(float);
};

class bilancia:public solido{
    private:
    float ps; //peso specifico
    public:
    bilancia();
    void assegna(float);
    float calcola(float, float);
};
int main()
{
    bilancia forma;
    float s=0.0, v=0.0, p=0.0;
    cout<<"Inserire la misura dello spigolo: ";
    cin>>s;
    forma.assegna(s);
    v=forma.calcola(s);
    cout<<"Il volume del cubo e': "<<v<<endl;
    cout<<"Inserire il peso specifico: ";
    cin>>p;
    forma.assegna(p);
    cout<<"Il peso del cubo e': "<<forma.calcola(v, p)<<endl;
    return 0;
}

solido::solido(){
    spigolo=0;
}

void solido::assegna(float s){
    spigolo=s;
}

float solido::calcola(float s){
    float v;
    v=pow(s, 3);
    return v;
}

bilancia::bilancia(){
    ps=0;
}

void bilancia::assegna(float p){
    ps=p;
}

float bilancia::calcola(float v, float p){
    float k=0.0;
    k=v*p;
    return k;
}
Essendo arrugginito in matematica non so se ho compreso bene il testo del problema, ma in ogni caso questo è ciò che ho prodotto.
Ringrazio anticipatamente per chi mi aiuterà

12 Risposte

  • Re: Problema c++ con OOP

    Puoi riportare gli errori di compilazione?
  • Re: Problema c++ con OOP

    Alexv ha scritto:


    Puoi riportare gli errori di compilazione?
    Ecco l'errore:

    main.cpp: In function ‘int main()’:
    main.cpp:36:22: error: no matching function for call to ‘bilancia::calcola(float&)’
    v=forma.calcola(s);
    ^
    main.cpp:27:11: note: candidate: float bilancia::calcola(float, float)
    float calcola(float, float);
    ^~~~~~~
    main.cpp:27:11: note: candidate expects 2 arguments, 1 provided
  • Re: Problema c++ con OOP

    La funzione
    float calcola(float, float);
    della classe bilancia richiede due argomenti, gliene hai passato uno:
    v=forma.calcola(s);
  • Re: Problema c++ con OOP

    Alexv ha scritto:


    La funzione
    float calcola(float, float);
    della classe bilancia richiede due argomenti, gliene hai passato uno:
    v=forma.calcola(s);
    Si appunto, forse qualcosa non mi è chiaro.. Il polimorfismo non è quando una classe base e classe derivata possono contenere due metodi con lo stesso nome e tipo/numero di argomenti diversi? Si chiama Overloading se non erro..
  • Re: Problema c++ con OOP

    Ciao, nonostante mi sia ripromesso più volte di farlo, si tratta di un argomento che non ho ancora affrontato e quindi non posso aiutarti, ma se ti può essere utile volevo consigliarti un corso di C++ che trovo davvero ben fatto, magari anche come integrazione al libro delle superiori che stai utilizzando:

    http://www-old.bo.cnr.it/corsi-di-informatica/corsoCstandard/Lezioni/01Indice.html
  • Re: Problema c++ con OOP

    cslash89 ha scritto:


    Si appunto, forse qualcosa non mi è chiaro.. Il polimorfismo non è quando una classe base e classe derivata possono contenere due metodi con lo stesso nome e tipo/numero di argomenti diversi? Si chiama Overloading se non erro..
    Sì è un overloading, una forma di polimorfismo statico che quindi viene risolto in fase di compilazione: se "forma" è un'istanza di "bilancia" e chiami "calcola" su di esso, il compilatore si aspetta di trovare i parametri richiesti nella sua dichiarazione in bilancia.
  • Re: Problema c++ con OOP

    Alexv ha scritto:


    cslash89 ha scritto:


    Si appunto, forse qualcosa non mi è chiaro.. Il polimorfismo non è quando una classe base e classe derivata possono contenere due metodi con lo stesso nome e tipo/numero di argomenti diversi? Si chiama Overloading se non erro..
    Sì è un overloading, una forma di polimorfismo statico che quindi viene risolto in fase di compilazione: se "forma" è un'istanza di "bilancia" e chiami "calcola" su di esso, il compilatore si aspetta di trovare i parametri richiesti nella sua dichiarazione in bilancia.
    Quindi la il codice corretto dovrebbe essere così giusto? (Gira senza problemi ma non so se è corretto):
    
    #include <iostream>
    #include <cmath>
    using namespace std;
    class solido{
        protected:
        float spigolo;
        public:
        solido();
        void assegna(float);
        float calcola(float);
    };
    
    class bilancia:public solido{
        private:
        float ps; //peso specifico
        public:
        bilancia();
        void assegna(float);
        float calcola(float, float);
    };
    int main()
    {
        solido forma;
        float s=0.0, v=0.0, p=0.0;
        cout<<"Inserire la misura dello spigolo: ";
        cin>>s;
        forma.assegna(s);
        v=forma.calcola(s);
        cout<<"Il volume del cubo e': "<<v<<endl;
        cout<<"Inserire il peso specifico: ";
        cin>>p;
        bilancia forma1;
        forma.assegna(p);
        cout<<"Il peso del cubo e': "<<forma1.calcola(v, p)<<endl;
        return 0;
    }
    
    solido::solido(){
        spigolo=0;
    }
    
    void solido::assegna(float s){
        spigolo=s;
    }
    
    float solido::calcola(float s){
        float v;
        v=pow(s, 3);
        return v;
    }
    
    bilancia::bilancia(){
        ps=0;
    }
    
    void bilancia::assegna(float p){
        ps=p;
    }
    
    float bilancia::calcola(float v, float p){
        float k=0.0;
        k=v*p;
        return k;
    }
    
    Ma perchè allora il compilatore comprende che la funzione Assegna prima è rivolta al solido e successivamente alla bilancia? Ho lasciato appositamente forma.assegna per entrambe le classi e non mi ha dato l'errore di compilazione di prima.
  • Re: Problema c++ con OOP

    Nippolo ha scritto:


    Ciao, nonostante mi sia ripromesso più volte di farlo, si tratta di un argomento che non ho ancora affrontato e quindi non posso aiutarti, ma se ti può essere utile volevo consigliarti un corso di C++ che trovo davvero ben fatto, magari anche come integrazione al libro delle superiori che stai utilizzando:

    http://www-old.bo.cnr.it/corsi-di-informatica/corsoCstandard/Lezioni/01Indice.html
    Grazie mille, ne terrò conto
  • Re: Problema c++ con OOP

    Perché forma è definito come solido e forma1 come bilancia. In quel caso si tratta di overriding (stessi parametri) e viene chiamata la prima definizione disponibile nella gerarchia partendo dal tipo a cui appartiene l'istanza a salire.
    Se vuoi chiamare il calcola di solido da un oggetto di tipo bilancia, devi fare:
    
    bilancia forma;
    v=forma.solido::calcola(s);
    
    Il motivo per cui il compilatore non è in grado di capirlo da solo nel caso di overloading non lo so.
  • Re: Problema c++ con OOP

    Partiamo dall'assioma che il C++ e' COMPLICATO/COMPLESSO/ROGNOSO perche' si porta dietro tutta una serire di decisioni che vanno dallo ""storico"" all' ""architetturaleper motivi di efficienza"".
    Era un MUST 10/20 anni fa, anche perche' non c'erano molti linguaggi di programmazione disponibili. Oggi e' un MUST per chi deve ""grattare il ferro"", cioe' deve sfruttare TUTTA la potenza di calcolo dell'hardware a disposizione.

    Il consiglio e' di lasciar perdere il C++ perche' ci sono molti linguaggi alternativi con cui imparare le basi della programmazione SENZA le complessia' subdole del C++. Nell'ordine: Python oppure Java/C# (linguaggi simili con complessita' simili)

    In ogni case, QUESTO problema e' uno di quei casi ROGNOSI da comprendere
    Una classe Solido consente di rappresentare una figura solida e di calcolarne il volume;
    DERIVARE da essa la classe Bilancia che consente di modificare il metodo per il calcolo, in
    modo da ottenere anche il peso del solido conoscendone il peso specifico (peso = volume
    x peso specifico).
    Nota: quel DERIVARE e' il termine sbagliato SE si vuole far capire il concetto di polimorfismo.
    La Bilancia NON DERIVA da Solido, MA DEVE USARE un Solido.

    Supponendo che quel ""deriva"" sia solo un errore ""italianico"" , l'esercizio dovrebbe svolgersi cosi'
    
    #include <iostream>
    #include <math.h>
    
    
    
    class Solido {
    public:
        virtual std::string v_whoami(){ return "Un solido"; }    // ATTENZIONE: NOTARE IL ""virtual""
        std::string         s_whoami(){ return "Un solido"; }
    
        virtual double v_volume(){ return 0; }    // ATTENZIONE: NOTARE IL ""virtual""
                double s_volume(){ return 0; }
    };
    
    class Cubo : public Solido {
        double lato;
    public:
        Cubo(double l): lato(l) { }
    
        virtual std::string v_whoami(){ return "Un cubo"; }    // ATTENZIONE: NOTARE IL ""virtual""
        std::string         s_whoami(){ return "Un cubo"; }
    
        virtual double v_volume(){ return lato*lato*lato; }
        double         s_volume(){ return lato*lato*lato; }
    };
    
    class Sfera : public Solido {
        double raggio;
    public:
        Sfera(double r) : raggio(r) {}
    
        virtual std::string v_whoami() { return "Una sfera"; }    // ATTENZIONE: NOTARE IL ""virtual""
        std::string         s_whoami() { return "Una sfera"; }
    
        virtual double v_volume() { return 4. / 3. * M_PI * raggio * raggio * raggio; }
                double s_volume() { return 4. / 3. * M_PI * raggio * raggio * raggio; }
    };
    
    class Bilancia {
    public:
        double v_peso(Solido *solido, double pesoSpecifico) {
            return pesoSpecifico * solido->v_volume();
        }
    
        double s_peso(Solido *solido, double pesoSpecifico) {
            return pesoSpecifico * solido->s_volume();
        }
    };
    
    
    SENZA il ""virtual"" il C++ tratta i metodi di una classe COME DELLE FUNZIONI che dipendono DAL TIPO DELLA VARIABILE, NON dal tipo del valore!
    La differenza e' questa
    
    
    
    
    void printWhoami(const char *name, Solido *p) {
        std::cout << "ptr di tipo Solido, valore di tipo ptr a " << name << ":" << std::endl
            << "    v:" << p->v_whoami() << std::endl
            << "    s:" << p->s_whoami() << std::endl;
    }
    
    void printCuboWhoami(const char *name, Cubo *p) {
        std::cout << "ptr di tipo Cubo, valore di tipo ptr a " << name << ":" << std::endl
            << "    v:" << p->v_whoami() << std::endl
            << "    s:" << p->s_whoami() << std::endl;
    }
    
    void printSferaWhoami(const char *name, Sfera *p) {
        std::cout << "ptr di tipo Sfera, valore di tipo ptr a " << name << ":" << std::endl;
        std::cout << "    v:" << p->v_whoami() << std::endl;
        std::cout << "    s:" << p->s_whoami() << std::endl;
    }
    
    
    int main() {
        Solido s;
        Cubo cu(2);
        Sfera sf(2);
    
        Solido *ps   = new Solido;
        Solido *pscu = new Cubo(2);
        Solido *pssf = new Sfera(2);
        Cubo   *pcu  = new Cubo(2);
        Sfera  *psf  = new Sfera(2);
    
        printWhoami("Solido", &s);
        printWhoami("Cubo", &cu);
        printWhoami("Sfera", &sf);
    
        std::cout <<std::endl;
    
        printWhoami("Solido", ps);
        printWhoami("Cubo", pscu);
        printWhoami("Sfera", pssf);
        printWhoami("Cubo", pcu);
        printWhoami("Sfera", psf);
    
        std::cout <<std::endl;
    
        printCuboWhoami("Cubo", &cu);
        printCuboWhoami("Cubo", (Cubo*)pscu);
        printCuboWhoami("Cubo", pcu);
    
        std::cout <<std::endl;
    
        printSferaWhoami("Sera", &sf);
        printSferaWhoami("Sfera", (Sfera*)pssf);
        printSferaWhoami("Sfera", psf);
    
    }
    
    
    Risultato
    
    ptr di tipo Solido, valore di tipo ptr a Solido:
        v:Un solido
        s:Un solido
    ptr di tipo Solido, valore di tipo ptr a Cubo:
        v:Un cubo
        s:Un solido
    ptr di tipo Solido, valore di tipo ptr a Sfera:
        v:Una sfera
        s:Un solido
    
    ptr di tipo Solido, valore di tipo ptr a Solido:
        v:Un solido
        s:Un solido
    ptr di tipo Solido, valore di tipo ptr a Cubo:
        v:Un cubo
        s:Un solido
    ptr di tipo Solido, valore di tipo ptr a Sfera:
        v:Una sfera
        s:Un solido
    ptr di tipo Solido, valore di tipo ptr a Cubo:
        v:Un cubo
        s:Un solido
    ptr di tipo Solido, valore di tipo ptr a Sfera:
        v:Una sfera
        s:Un solido
    
    ptr di tipo Cubo, valore di tipo ptr a Cubo:
        v:Un cubo
        s:Un cubo
    ptr di tipo Cubo, valore di tipo ptr a Cubo:
        v:Un cubo
        s:Un cubo
    ptr di tipo Cubo, valore di tipo ptr a Cubo:
        v:Un cubo
        s:Un cubo
    
    ptr di tipo Sfera, valore di tipo ptr a Sera:
        v:Una sfera
        s:Una sfera
    ptr di tipo Sfera, valore di tipo ptr a Sfera:
        v:Una sfera
        s:Una sfera
    ptr di tipo Sfera, valore di tipo ptr a Sfera:
        v:Una sfera
        s:Una sfera
    
    Process finished with exit code 0
    
    
    Gli ""strani comporamenti"" sono legati a DUE concetti:

    1) il TIPO dell'oggetto conosciuto dalla funzione
    2) il fatto che un metodi sia dichiarato O MENO ""virtual""

    I metodi ""virtual"" SOSTITUISCONO l'equivalente metodo della classe base
    I metodi ""NON virtual"" sono scelti in base al TIPO dell'oggetto CONOSCIUTO dalla funzione.

    A spiegarlo cosi' e' un ""casino""
  • Re: Problema c++ con OOP

    Ma una sottoclasse che eredita con specificatore public È anche una classe base, quindi non vedo motivi per cui il compilatore non debba considerare tutti gli overloading di una funzione presenti nella gerarchia quando chiamati dalla sottoclasse, ma ti costringe a specificare il nome della classe base.
    Di certo non per problemi di ambiguità, visto che ogni funzione ha una firma diversa. Forse per evitare errori del programmatore?
  • Re: Problema c++ con OOP

    migliorabile ha scritto:


    Partiamo dall'assioma che il C++ e' COMPLICATO/COMPLESSO/ROGNOSO perche' si porta dietro tutta una serire di decisioni che vanno dallo ""storico"" all' ""architetturaleper motivi di efficienza"".
    Era un MUST 10/20 anni fa, anche perche' non c'erano molti linguaggi di programmazione disponibili. Oggi e' un MUST per chi deve ""grattare il ferro"", cioe' deve sfruttare TUTTA la potenza di calcolo dell'hardware a disposizione.

    Il consiglio e' di lasciar perdere il C++ perche' ci sono molti linguaggi alternativi con cui imparare le basi della programmazione SENZA le complessia' subdole del C++. Nell'ordine: Python oppure Java/C# (linguaggi simili con complessita' simili)

    In ogni case, QUESTO problema e' uno di quei casi ROGNOSI da comprendere
    Una classe Solido consente di rappresentare una figura solida e di calcolarne il volume;
    DERIVARE da essa la classe Bilancia che consente di modificare il metodo per il calcolo, in
    modo da ottenere anche il peso del solido conoscendone il peso specifico (peso = volume
    x peso specifico).
    Nota: quel DERIVARE e' il termine sbagliato SE si vuole far capire il concetto di polimorfismo.
    La Bilancia NON DERIVA da Solido, MA DEVE USARE un Solido.

    Supponendo che quel ""deriva"" sia solo un errore ""italianico"" , l'esercizio dovrebbe svolgersi cosi'
    
    #include <iostream>
    #include <math.h>
    
    
    
    class Solido {
    public:
        virtual std::string v_whoami(){ return "Un solido"; }    // ATTENZIONE: NOTARE IL ""virtual""
        std::string         s_whoami(){ return "Un solido"; }
    
        virtual double v_volume(){ return 0; }    // ATTENZIONE: NOTARE IL ""virtual""
                double s_volume(){ return 0; }
    };
    
    class Cubo : public Solido {
        double lato;
    public:
        Cubo(double l): lato(l) { }
    
        virtual std::string v_whoami(){ return "Un cubo"; }    // ATTENZIONE: NOTARE IL ""virtual""
        std::string         s_whoami(){ return "Un cubo"; }
    
        virtual double v_volume(){ return lato*lato*lato; }
        double         s_volume(){ return lato*lato*lato; }
    };
    
    class Sfera : public Solido {
        double raggio;
    public:
        Sfera(double r) : raggio(r) {}
    
        virtual std::string v_whoami() { return "Una sfera"; }    // ATTENZIONE: NOTARE IL ""virtual""
        std::string         s_whoami() { return "Una sfera"; }
    
        virtual double v_volume() { return 4. / 3. * M_PI * raggio * raggio * raggio; }
                double s_volume() { return 4. / 3. * M_PI * raggio * raggio * raggio; }
    };
    
    class Bilancia {
    public:
        double v_peso(Solido *solido, double pesoSpecifico) {
            return pesoSpecifico * solido->v_volume();
        }
    
        double s_peso(Solido *solido, double pesoSpecifico) {
            return pesoSpecifico * solido->s_volume();
        }
    };
    
    
    SENZA il ""virtual"" il C++ tratta i metodi di una classe COME DELLE FUNZIONI che dipendono DAL TIPO DELLA VARIABILE, NON dal tipo del valore!
    La differenza e' questa
    
    
    
    
    void printWhoami(const char *name, Solido *p) {
        std::cout << "ptr di tipo Solido, valore di tipo ptr a " << name << ":" << std::endl
            << "    v:" << p->v_whoami() << std::endl
            << "    s:" << p->s_whoami() << std::endl;
    }
    
    void printCuboWhoami(const char *name, Cubo *p) {
        std::cout << "ptr di tipo Cubo, valore di tipo ptr a " << name << ":" << std::endl
            << "    v:" << p->v_whoami() << std::endl
            << "    s:" << p->s_whoami() << std::endl;
    }
    
    void printSferaWhoami(const char *name, Sfera *p) {
        std::cout << "ptr di tipo Sfera, valore di tipo ptr a " << name << ":" << std::endl;
        std::cout << "    v:" << p->v_whoami() << std::endl;
        std::cout << "    s:" << p->s_whoami() << std::endl;
    }
    
    
    int main() {
        Solido s;
        Cubo cu(2);
        Sfera sf(2);
    
        Solido *ps   = new Solido;
        Solido *pscu = new Cubo(2);
        Solido *pssf = new Sfera(2);
        Cubo   *pcu  = new Cubo(2);
        Sfera  *psf  = new Sfera(2);
    
        printWhoami("Solido", &s);
        printWhoami("Cubo", &cu);
        printWhoami("Sfera", &sf);
    
        std::cout <<std::endl;
    
        printWhoami("Solido", ps);
        printWhoami("Cubo", pscu);
        printWhoami("Sfera", pssf);
        printWhoami("Cubo", pcu);
        printWhoami("Sfera", psf);
    
        std::cout <<std::endl;
    
        printCuboWhoami("Cubo", &cu);
        printCuboWhoami("Cubo", (Cubo*)pscu);
        printCuboWhoami("Cubo", pcu);
    
        std::cout <<std::endl;
    
        printSferaWhoami("Sera", &sf);
        printSferaWhoami("Sfera", (Sfera*)pssf);
        printSferaWhoami("Sfera", psf);
    
    }
    
    
    Risultato
    
    ptr di tipo Solido, valore di tipo ptr a Solido:
        v:Un solido
        s:Un solido
    ptr di tipo Solido, valore di tipo ptr a Cubo:
        v:Un cubo
        s:Un solido
    ptr di tipo Solido, valore di tipo ptr a Sfera:
        v:Una sfera
        s:Un solido
    
    ptr di tipo Solido, valore di tipo ptr a Solido:
        v:Un solido
        s:Un solido
    ptr di tipo Solido, valore di tipo ptr a Cubo:
        v:Un cubo
        s:Un solido
    ptr di tipo Solido, valore di tipo ptr a Sfera:
        v:Una sfera
        s:Un solido
    ptr di tipo Solido, valore di tipo ptr a Cubo:
        v:Un cubo
        s:Un solido
    ptr di tipo Solido, valore di tipo ptr a Sfera:
        v:Una sfera
        s:Un solido
    
    ptr di tipo Cubo, valore di tipo ptr a Cubo:
        v:Un cubo
        s:Un cubo
    ptr di tipo Cubo, valore di tipo ptr a Cubo:
        v:Un cubo
        s:Un cubo
    ptr di tipo Cubo, valore di tipo ptr a Cubo:
        v:Un cubo
        s:Un cubo
    
    ptr di tipo Sfera, valore di tipo ptr a Sera:
        v:Una sfera
        s:Una sfera
    ptr di tipo Sfera, valore di tipo ptr a Sfera:
        v:Una sfera
        s:Una sfera
    ptr di tipo Sfera, valore di tipo ptr a Sfera:
        v:Una sfera
        s:Una sfera
    
    Process finished with exit code 0
    
    
    Gli ""strani comporamenti"" sono legati a DUE concetti:

    1) il TIPO dell'oggetto conosciuto dalla funzione
    2) il fatto che un metodi sia dichiarato O MENO ""virtual""

    I metodi ""virtual"" SOSTITUISCONO l'equivalente metodo della classe base
    I metodi ""NON virtual"" sono scelti in base al TIPO dell'oggetto CONOSCIUTO dalla funzione.

    A spiegarlo cosi' e' un ""casino""
    Ciao e grazie per la tua risposta e svolgimento dell'esercizio molto esaustivi. Premetto, come ho scritto già che sto studiando da un libro per scuole superiori, quindi la parte del "POLIMORFISMO" che c'è sul libro non tratta le funzioni virtuali, quindi per certo non sono richieste nello svolgimento dell'esercizio. Per quanto riguarda la quantità di solidi, sicuramente davo per scontato che richiedesse qualche figura solida in più, magari anche una piramide, una sfera, da poter poi scegliere con lo switch quale solido prendere in considerazione per i calcoli, ma per motivi di tempo, ho semplificato l'esecuzione dell'esercizio prendendo in considerazione solo un cubo. Per quanto riguarda invece l'italiano dell'esercizio credo proprio che intenderre derivare la classe bilancia da quella solido essendo esercizi di semplice esecuzione. Per quanto riguarda la tua versione invece,è sicuramente una versione svolta in maniere impeccabile e professionale, ma non essendo ancora arrivato a questo punto degli studi per il momento lo terrò da parte come esempio per quando ci arriverò, sicuramente con un altro libro di testo più approfondito
Devi accedere o registrarti per scrivere nel forum
12 risposte