Maurizio Tomasi
Martedì 22 Ottobre 2024
La spiegazione dettagliata degli esercizi si trova qui: carminati-esercizi-05.html.
Come al solito, queste slides, che forniscono suggerimenti addizionali rispetto alla lezione di teoria, sono disponibili all’indirizzo ziotom78.github.io/tnds-tomasi-notebooks.
Posizione
Particella
ed
Elettrone
CampoVettoriale
e
PuntoMateriale
Alcune funzioni utili disponibili in
<cmath>
:
Per maggiori informazioni, eseguire man
da
terminale
Consultate Wikipedia per comprendere la logica di atan2
.
bool are_close(double calculated, double expected, double epsilon = 1e-7) {
return fabs(calculated - expected) < epsilon;
}
void test_coordinates(void) {
Posizione p{1, 2, 3};
assert(are_close(p.getX(), 1.0));
assert(are_close(p.getY(), 2.0);
assert(are_close(p.getZ(), 3.0);
assert(are_close(p.getR(), 3.7416573867739));
assert(are_close(p.getPhi(), 1.1071487177941);
assert(are_close(p.getTheta(), 0.64052231267943);
assert(are_close(p.getRho(), 2.2360679774998);
cerr << "The coordinates work correctly! 🥳\n";
}
L’esempio usa new
per creare puntatori:
Particella a{1., 1.6e-19};
Elettrone *e{new Elettrone{}};
CorpoCeleste *c{new CorpoCeleste{"Terra", 6.0e24, 6.4e6}};
Questo è utile per lo scopo dell’esercizio (comprendere come funziona l’ereditarietà).
In un vero programma l’uso di new
e
delete
espliciti andrebbe però limitato il più possibile (e
in questi pochissimi casi, andrebbe comunque usato solamente in
costruttori/distruttori di classi, mai nel main
).
Estratto dall’articolo C++ is the next C++, che propone una nuova «modalità» di compilazione del C++ in cui disabilitare (quasi) del tutto i puntatori.
void test_coulomb_law(void) {
// 0.5 µC charge with no mass (irrelevant for the electric field)
PuntoMateriale particella1{0.0, 5e-7, 5, 3, -2};
Posizione p{-2, 4, 1};
CampoVettoriale V{particella.CampoElettrico(p)};
assert(are_close(V.getFx(), -69.41150052142065));
assert(are_close(V.getFy(), 9.915928645917235));
assert(are_close(V.getFz(), 29.747785937751708));
cerr << "Coulomb's law works correctly! 🥳\n";
}
void test_newton_law(void) {
// 10⁹ tonnes, without charge (irrelevant for the gravitational field)
PuntoMateriale particella1{1e12, 0, 5, 3, -2};
Posizione p{-2, 4, 1};
CampoVettoriale V{particella.CampoElettrico(p)};
assert(are_close(V.getFx(), -1.0302576701177));
assert(are_close(V.getFy(), 0.14717966715968));
assert(are_close(V.getFz(), 0.44153900147903));
cerr << "Newton's law works correctly! 🥳\n";
}
Per inizializzare i membri di una classe in C++ esistono due possibilità:
I due metodi non sono equivalenti!
Costruiamo una classe DaylightPeriod
che contiene al
suo interno due variabili dello stesso tipo Time
, ma
inizializzate in modo diverso:
Per capire cosa succede, definiamo Time
in modo che
stampi a video un messaggio ogni volta che viene invocato un suo
metodo.
Se ora creiamo nel main
una variabile
DaylightPeriod
, vedremo cosa accade nel costruttore:
L’output del programma è il seguente:
Call to Time constructor with time "7:00"
Time() called with no arguments
Call to Time constructor with time "21:00"
Call to Time::operator=
Ricordiamo come abbiamo definito la classe
DaylightPeriod
, e in particolare il costruttore:
Si dovrebbe evitare di inizializzare gli oggetti nel corpo del costruttore, perché il C++ richiede che tutte le variabili membro siano inizializzate prima che il costruttore di una classe sia eseguito.
Di conseguenza, se si usano i «vecchi» assegnamenti gli oggetti con costruttore vengono inizializzati due volte.
Se avete scelta, l’inizializzazione con i due punti
(:
) è sempre da preferire: