[C++] Approccio alle classi

Super Squirrel
Avrei alcuni dubbi in riferimento a questo mio primo approccio alle classi:

#include <iostream>
#include "math.h"

class point
{
    private:
        double x, y;
    public:
        point(double = 0, double = 0);
        double get_distance();
        double get_distance(point);
        friend std::ostream& operator <<(std::ostream&, const point&);
        friend std::istream& operator >>(std::istream&, point&);
};


#include "point.h"

point::point(double x_0, double y_0)
{
    x = x_0;
    y = y_0;
}

double point::get_distance()
{
    return sqrt(pow(x , 2) + pow(y , 2));
}

double point::get_distance(point P_0)
{
    return sqrt(pow(x - P_0.x , 2) + pow(y - P_0.y , 2));
}

std::ostream& operator <<(std::ostream& out, const point& P_0)
{
    out << "(" << P_0.x << "," << P_0.y << ")";
    return out ;
}

std::istream& operator >>(std::istream& in, point& P_0)
{
    std::cout << "ASCISSA PUNTO: ";
    in >> P_0.x;
    std::cout << "ORDINATA PUNTO: ";
    in >> P_0.y;
    return in;
}


#include <iostream>
#include "point.h"

using namespace std;

int main()
{
    point A(3, 4), B;
    cout << "A" << A <<endl;
    cin >> A;
    cout << "A" << A << endl << "B" << B;
    cout << endl << "AB = " << A.get_distance(B);
    cout << endl << "AO = " << A.get_distance();
}


- sembra funzionare, ma vorrei sapere se ho commesso qualche errore ed eventuali consigli di "buona programmazione".
- per quanto riguarda il costruttore ho evitato l'overloading delle funzioni ricorrendo a funzioni con parametri di default, non riesco invece a fare la stessa cosa con la funzione get_distance. in pratica ho dovuto scrivere due funzioni, una per avere la distanza del generico punto dall'origine e una per avere la distanza del generico punto da un altro punto.
Il problema è nel prototipo, ho provato con __double get_distance(point(0, 0));__ ma non funziona, come mai?
- i prossimi argomenti che ho intenzione di affrontare sono l'ereditarietà e il polimorfismo delle classi. suggerimenti su come potrei esercitarmi ad applicarli alla classe point appena creata?

Risposte
apatriarca
Stai imparando il linguaggio da solo o stai seguendo un corso universitario? E' infatti importante osservare che NON è così che si scrive un programma. Il fatto che il C++ o un qualche altro linguaggio forniscano una funzionalità non significa che vada usata per forza. La programmazione ad oggetti è insegnata malissimo e seguendo i criteri usati nei libri e nei corsi di base si finisce solo per scrivere pessimi linguaggi da ogni punto di vista (OOD compreso). Un primo problema è quello di invertire la relazione che esiste tra il problema da risolvere e il programma. Normalmente si dovrebbe partire da un problema e si dovrebbe costruire il programma che risolve tale problema. Nel OOP spesso si parte da una modellazione astratta del contesto in cui si trova il problema in modo da definire il problema in termini che il programma può comprendere. Facendo in questo modo si arriva ad un codice che è più complicato e più lento di quanto potrebbe essere.

Veniamo a questo punto al problema "tecnico". Il prototipo va scritto come segue:
double get_distance(point P_0 = point());

Non mi è invece chiaro perché hai deciso di voler evitare l'overloading comunque. Scrivere una funzione separata in casi come questo può effettivamente avere senso (eviti delle operazioni aggiuntive inutili).

pow(x, 2) è inguardabile. Scrivi semplicemente x*x..

L'operatore >> non ha senso. Non è detto che lo stream sia da console e che abbia quindi senso chiedere qualcosa all'utente..

In un caso del genere avrei semplicemente usato una struttura con tutto pubblico e funzioni non membro. In effetti non vedo alcuna ragione per cui la posizione debba essere nascosta. Aggiungi almeno delle funzioni membro per poterle recuperare. Eventualmente aggiungendo anche altre funzioni per ottenere le coordinate polari per esempio. Sinceramente non vedo come possa aver senso usare ereditarietà o polimorfismo con una classe del genere. Se lo desideri possiamo fornirti esempi più realistici che puoi usare.

Super Squirrel
Studio ingegneria ambientale e fondamenti di informatica era uno degli esami a scelta del corso. Studiando per l'esame, sempre da autodidatta, mi sono appassionato molto alla programmazione, e quindi, anche dopo aver sostenuto l'esame, mi sono divertito a creare tanti altri programmini.
Ultimamente la consapevolezza che una migliore conoscenza del linguaggio mi avrebbe permesso sia di scrivere i suddetti programmi in modo più chiaro, sia di avvicinarmi a progetti molto più complessi(come l'implementazione di un gioco di carte), mi ha spinto ad approfondire lo studio del C++.
Ho letto del materiale trovato online e ho provato ad implementare questa classe giusto per testare alcune delle nuove funzionalità appena studiate (tipo parametri di default, overload delle funzioni e degli operatori, classi...). Per questo il programma in sè non ha molto senso e alcune scelte sembrano forzate.
Alla fine penso che bisogna conoscere tutte le funzionalità messe a disposizione dal linguaggio in modo da poter scegliere la strada migliore (che credo consista nel giusto compromesso tra efficienza e chiarezza del codice) per la risoluzione del problema (che ovviamente deve sempre essere il punto di partenza).

Per quanto riguarda il prototipo della funzione get_distance, l'ho provato e funziona, grazie.

Qualsiasi idea su come esercitarmi con ereditarietà e polimorfismo è ben accetta.

apatriarca
Se desideri implementare un gioco di carte, prova a fare quello. Parto subito però nell'invitarti a non cercare di forzare le idee dell'OOP in questo caso. Ci potrebbe infatti essere la tendenza di usare classi per cose come i semi. Ma i semi sono solo quattro e possono essere rappresentati meglio usando una semplice enumerazione. In effetti ogni carta potrebbe essere rappresentata semplicemente come un semplice intero, per esempio \( \mathrm{seme} * 128 + \mathrm{valore} \). Potresti usare una classe per rappresentare le carte (ma non è necessario*). Di certo non userei l'ereditarietà o il polimorfismo in questo caso.

In effetti l'ereditarietà la uso normalmente quasi solo in una singola situazione: creazione di interfacce grafiche. In tutte le altre situazioni esistono soluzioni MOLTO migliori. Ho l'impressione che il manuale o le fonti che tu stia usando siano un po' antiquate. Esistono infatti diverse funzionalità a mio parere molto più importanti di quelle che stai studiando nel C++ moderno. Per esempio: template, container standard, lambda..

* Può in effetti complicare alcune cose come l'ordinamento. Usando dei semplici numeri interi per le carte puoi usare std::sort (si trova in algorithm) senza dover implementare alcuna funzione di confronto. Tuttavia alcune cose come accedere al seme o valore potrebbero essere più facile con una classe. Ma dipende dall'implementazione.

Super Squirrel
Grazie per i consigli, comunque i template ci sono nel corso che sto seguendo, mentre i containers standard e i lambda no, magari quando finisco troverò qualcosa online al riguardo.

Per quanto riguarda il gioco di carte credo sia ancora presto e comunque mi riferivo a Yu-Gi-Oh, che dovrebbe essere molto più complesso da implementare.

Per il momento continuerò a studiare ereditarietà e polimorfismo, magari poi posterò qualche tentativo di utilizzo per eventuali correzioni e consigli.

Rispondi
Per rispondere a questa discussione devi prima effettuare il login.