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""