[classi c++ ] - Classe Quadrato

84Valery84
Ciao a tutti

Sono alle prime armi con la programmazione c++ e in particolare con le classi. Ho provato a fare questo esercizio che mi chiede di implementare una classe quadrato e di calcolare l'area, il perimetro e la diagonale.
Onestamente non so se ho commesso errori particolari, se ho sbagliato tutto o se non c'ho capito nulla.
Il compilare mi segnala questo errore, ( cioè una parentesi prima del costruttore) : expected unqualified-id before '{' token.

Poi volevo, se possibile, avere un chiarimento: In alcuni testi mi viene detto che il costruttore va messo al di fuori della classe, in altri invece dentro. Si può sapere dove devo piazzarlo?! :(

Di seguito posto il codice, se qualcuno può essermi di aiuto dandomi qualche dritta ne sarei davvero grata :)

grazie mille a tutti. :)

PS: mi scuso in anticipo se ho commesso banalità e cose obbrobriose :oops:

# include <iostream>
# include <cmath>
using namespace std;

class Quadrato {
	public:
	Quadrato (); // costruttore
	float Perimetro();
	float Area ();
	float Diagonale();
	private:
	float lato;


};
{

	Quadrato::Quadrato() {float=0;}
	void Quadrato::Area (float){
		Area= lato*lato;
	}
	void Quadrato::Perimetro(float){
		Perimetro= lato*4;
	}
	void Quadrato::Diagonale(float){
		Diagonale= sqrt(lato*lato+lato*lato);
	}
}

int main() {
	Quadrato q;
	cout << "Inserisci il valore del lato: \n ";
	cin >> lato;
	cout << "L'area è: " << Area << q.Area();
	cout<<"\nIl Perimetro è: " << Perimetro << q.Perimetro();
	cout<<"\nLa diagonale è: " << Diagonale << q.Diagonale();

	cout << endl;
	return 0;

}

Risposte
vict85
A me sembra che tu non abbia capito come funzione una funzione. Le classi sono una conseguenza.

Un funzione in C++ ha la struttura:

return_type nome_funzione (argument_list)
{
    [...]
    return Output_var;
}


Questa scrittura va interpretata nel seguente modo:

La funzione ha nome nome_funzione e riceve dalla funzione che lo chiama certe variabili espresse nella argument_list e dopo una serie di operazioni risponde Output_var che è una variabile ti tipo return_type.

Esempio:

float doppio(float a)
{
   return 2*a;
}


è una funzione che riceve un float chiamato a e risponde con un float che ha il doppio del valore di a.

Venendo al tuo codice. Tu scrivi:
void Quadrato::Area (float)
{
     Area= lato*lato;
}

mentre, siccome Area è una normalissima funzione avresti dovuto scrivere:
float Quadrato::Area ()
{
     return lato*lato;
}

oppure
float Quadrato::Area ()
{
     const float area = lato*lato;
     return area;
}



Ora veniamo alla differenza tra una qualsiasi funzione ed una funzione membro di una classe: la funzione membro può lavorare sulle variabili private della classe mentre una funzione qualsiasi non le vede. Inoltre cambia lievemente il modo in cui viene chiamata ed è logicamente connessa al particolare oggetto. Ma per il resto va trattata come una classica funzione.

84Valery84
oi, grazie mille adesso mi rivedo meglio i concetti e provo a modificare il programma diversamente. Magari lo posto poi per avere un ulteriore riscontro.
Sei stato gentilissimo :)
Thanks ;)

vict85
Non avevo visto una cosa... Nel main:

int main() {
	Quadrato q;
	cout << "Inserisci il valore del lato: \n ";
	cin >> lato;
	cout << "L'area è: " << Area << q.Area();
	cout<<"\nIl Perimetro è: " << Perimetro << q.Perimetro();
	cout<<"\nLa diagonale è: " << Diagonale << q.Diagonale();

	cout << endl;
	return 0;

}


Il compilatore dovrebbe dirti che lato, Area, Perimetro e Diagonali non sono stati definiti... Ed infatti non hanno alcun senso.

Devi scrivere:


int main() {
        float lato;
	cout << "Inserisci il valore del lato: \n ";
	cin >> lato;
        Quadrato q(lato);
	cout << "L'area è: " << q.Area();
	cout<<"\nIl Perimetro è: " << q.Perimetro();
	cout<<"\nLa diagonale è: " << q.Diagonale();

	cout << endl;
	return 0;

}


E manca il costruttore Quadrato(float) oppure una funzione per cambiare lato. La variabile lato esiste solo dentro q ed è invisibile per il resto del codice.

84Valery84
Si, visto oggi infatti quella parte sono riuscita a modificarla.

Allora ho rivisto il programma e cercato di seguire i tuoi suggerimenti, preziosissimi tra l'altro.

Il punto è questo:
Ho aggiunto il costruttore e ho messo gli oggetti della classe in modo da renderli coerenti ( questo è quanto esce fuori dal mio libro di testo, ho semplicemente copiato);

Altro punto: ho eliminato la funzione diagonale perchè mi dava un errore strano e che onestamente non sono riuscita a risolvere. Ho lavorato dunque solo con area e perimetro.
Il problema è il seguente; Il programma compila senza errori ma non sono affatto sicura, poichè, pur compilando, l' output del mio programma è :

Inserisci il valore del lato: 2.1

L'area è: 0
Il perimetro è: 2.98241e-038



Onestamente non so che pensare....

Il codice è:
#include <iostream>
#include <cmath>
using namespace std;

class Quadrato
 {
 public:
	Quadrato(); // costruttore
		float Area ();
	float Perimetro ();

 private:
	float lato;
	float area;
	float perimetro;

 };

Quadrato::Quadrato() // metto gli oggetti di Quadrato in uno stato coerente
{
	area=0;
	perimetro=0;
}

float Quadrato::Area()
{
	const float area=lato*lato;
	return area;
}

float Quadrato::Perimetro()
{
	const float perimetro= lato*4;
	return perimetro;
};

int main () { // istanzio la classe
	Quadrato q;
	float lato;
	cout << "Inserisci il valore del lato: ";
	cin >> lato;
	cout << "\nL'area è: " << q.Area();
	cout << "\nIl perimetro è: " << q.Perimetro();
	cout << endl;
	return 0;

}



Ho fatto diverse prove e aggiusti, ma aggiustavo una cosa e ne rovinavo un'altra! Sono ancora una frana :(

Grazie mille dell'aiuto :)

claudio862
La variabile float lato definita nel main() non è la stessa definita nella classe Quadrato. Quindi il valore di "lato" nelle funzioni Area() e Perimetro() non è quello inserito dall'utente. Devi in qualche modo assegnare alla variabile lato della classe Quadrato quello che leggi da tastiera:

class Quadrato
 {
 public:
   Quadrato(); // costruttore
   float Area ();
   float Perimetro ();
   void setLato(float nuovoLato);   // <- Questa funzione assegna un valore alla variabile lato

 private:
   float lato;
   float area;
   float perimetro;

 };

// Qua metti le altre funzioni della classe Quadrato

void Quadrato::setLato(float nuovoLato)
{
   lato = nuovoLato; // "lato" qua è la variabile della classe Quadrato
}

int main () {
   Quadrato q;
   float lato;
   cout << "Inserisci il valore del lato: ";
   cin >> lato;
   q.setLato(lato);  // <- Qua assegni a q.lato il valore inserito dall'utente
   cout << "\nL'area è: " << q.Area();
   cout << "\nIl perimetro è: " << q.Perimetro();
   cout << endl;
   return 0;

}


Meglio ancora sarebbe passare il valore del lato nel costruttore:

class Quadrato
 {
 public:
   Quadrato(float nuovoLato); // costruttore
   // altre funzioni e variabili...
 };

Quadrato::Quadrato(float nuovoLato)
{
   lato = nuovoLato; // "lato" qua è la variabile della classe Quadrato
}

int main () {
   float lato;
   cout << "Inserisci il valore del lato: ";
   cin >> lato;
   Quadrato q(lato);
   cout << "\nL'area è: " << q.Area();
   cout << "\nIl perimetro è: " << q.Perimetro();
   cout << endl;
   return 0;

}


Altre osservazioni:
Hai definito altre due variabili "area" e "perimetro" nella classe Quadrato, che non usi. Nelle funzioni Area() e Perimetro() calcoli area e perimetro al momento e li restituisci. Una via alternativa sarebbe salvare i valori di area e perimetro nelle variabili "area" e "perimetro" quando cambi il valore di "lato", e nelle funzioni Area() e Perimetro() restituire semplicemente i valori delle variabili:

void Quadrato::setLato(float nuovoLato)
{
   lato = nuovoLato;
   area=lato*lato;
   perimetro= lato*4;
}
float Quadrato::Area()
{
   return area;
}

float Quadrato::Perimetro()
{
   return perimetro;
};

84Valery84
grazie! adesso mi studio questa cosa e vediamo se riesco finalmente ad acquistare un pò più di padronanza , mi rendo conto che ci sono ancora alcune cose e funzionamenti che devo ben capire.
Avevo pensato anche alla funzione set per assegnare un valore... grazie mille
Vi farò sapere come è andata :)
buon w.e.

84Valery84
@claudio86

Grazie mille mi state chiarendo tante cose. Ho modificato secondo le tue dritte e il progromma mi restituisce valori normali :D.
Purtroppo non mi è chiara concettualmente ancora una cosa: perchè la variabile lato del main e quella della classe quadrato sono differenti? cioè in un certo senso non devono essere "legate"?! E quindi concettualmente io nel main cosa devo fare?!
Ho capito come si dichiara l'interfaccia della classe e ho capito cosa devono fare e come più o meno vanno usate le funzioni che creo nella classe. Ma nel main sostanzialmente cosa devo "dir di fare" ?
grazie ancora a tutti. :)

claudio862
"Pandora":
Purtroppo non mi è chiara concettualmente ancora una cosa: perchè la variabile lato del main e quella della classe quadrato sono differenti? cioè in un certo senso non devono essere "legate"?! E quindi concettualmente io nel main cosa devo fare?!

La classe Quadrato ha tre variabili: lato, area e perimetro. Questo significa che da un certo punto di vista un oggetto di tipo Quadrato non è altro che una tripletta di float alla quale sono associate delle operazioni (le funzioni della classe).
Tu nel main dichiari una variabile float lato e una variabile Quadrato q:

int main()
{
    float lato;
    Quadrato q;
...
}


Concettualmente (a parte l'azione del costruttore) è come fare:

int main()
{
    float lato;
    
    float q_lato;
    float q_area;
    float q_perimetro;
...
}


Come vedi la variabile lato nella funzione main() è completamente indipendente dalla variabile lato dell'oggetto q. Hanno lo stesso nome ma questo è irrilevante perché la prima è locale alla funzione main(), mentre la seconda è locale alla classe Quadrato (o meglio all'oggetto q, ogni oggetto della classe Quadrato ha una sua variabile lato).


Ho capito come si dichiara l'interfaccia della classe e ho capito cosa devono fare e come più o meno vanno usate le funzioni che creo nella classe. Ma nel main sostanzialmente cosa devo "dir di fare" ?
grazie ancora a tutti. :)


Una volta che hai definito le tue classi, nel main() devi creare gli oggetti necessari, inizializzarli e interagire con loro (= chiamare le loro funzioni). Ovviamente dipende dal caso specifico, quindi è difficile fare esempi; magari prova con qualche esercizio e se hai problemi chiedi qua.

84Valery84
Allora, mi sono cimentata in un esercizio simile.
Il testo dice di creare una classe Rettangolo che ha come attributi base e altezza ognuno delle quali per default valgono 1.
Ha due funzioni membro che calcolano il perimetro e l'area del rettangolo e le funzioni SET e GET per base e altezza che verificano che entrambi siano numeri in virgola mobile.
Io l'ho svolto nel modo seguente. Ho qualche perplessità su come ho utilizzato le funzioni set e get ( il supporto didattico non dice molto a riguardo) e soprattutto ho qualche difficoltà con il main. Ti posto il codice e l'output se per favore puoi controllare dov'è che faccio pasticci te ne sarei molto grata :)

output:
**** Internal Builder is used for build               ****
g++ -O0 -g3 -Wall -c -fmessage-length=0 -oclasse rectangle.o ..\classe rectangle.cpp
..\classe rectangle.cpp: In member function 'float Rettangolo::setBase(float)':
..\classe rectangle.cpp:43: warning: no return statement in function returning non-void
..\classe rectangle.cpp: In member function 'float Rettangolo::setAltezza(float)':
..\classe rectangle.cpp:49: warning: no return statement in function returning non-void
g++ -oCLASSE RECTANGLE.exe classe rectangle.o
classe rectangle.o: In function `main':
C:\Users\Supervale\workspace\CLASSE RECTANGLE\Debug/../classe rectangle.cpp:61: undefined reference to `Rettangolo::Rettangolo()'
collect2: ld returned 1 exit status
Build error occurred, build is stopped
Time consumed: 511  ms.  




Codice implementato:
#include <iostream>
#include <cmath>
using namespace std;
class Rettangolo {
public:
	//costruttore
	Rettangolo();
	// metodi
	float Area();
	float Perimetro();
	float setAltezza(float);
	float setBase(float);
	float getAltezza();
	float getBase();
private:
	float base;
	float altezza;
	float a;
	float b;
};

// sviluppo i metodi della classe
float Rettangolo::Area() // calcolo area
{
	return a*b;
}
float Rettangolo::Perimetro() // calcolo perimetro
		{
	return (a+b)*2;
}
// devo ora verificare con set e get se i valori di base e altezza sono numeri float
float Rettangolo::setBase(float b)
{
	base = (b);
	base=(b >=0.0 && b<20.0)? :0;
}

float Rettangolo :: setAltezza(float a)
{
	altezza = (a);
		altezza =(a >=0.0 && a<20.0)? :0;
}
	// funzione get : restituiso il valore di base e altezza

float Rettangolo::getBase()
	{
		return base;
	}
	float Rettangolo:: getAltezza()
	{
	return altezza;
}
int main () {
	Rettangolo r;
	float base;
	float altezza;
	cout <<"inserisci un valore per la base: ";
	cin >> base;
	cout <<"inserisci un valore per l'altezza: ";
		cin >> altezza;
	cout << "L'area è:" << r.Area();
 cout << "Il perimetro è " << r.Perimetro();
 return 0;

}



grazie infinite.

84Valery84
ah, riguardandolo devo aggiungere il costruttore perchè l'ho menzionato nell 'interfaccia ma non l'ho più inserito. Tramite quello dovrei porre base e altezza pari a 1

claudio862
Ok, un po' di cose:
1) L'errore "undefined reference to `Rettangolo::Rettangolo()'" ti dice che il linker non riesce a trovare la funzione Rettangolo::Rettangolo(), cioè il costruttore.
A volte questo tipo di errore è abbastanza difficile da capire. Il compilatore sa che esiste la funzione, perché l'hai dichiarata nella definizione della classe, ma non l'ha trovata, perché in effetti non l'hai definita da nessuna parte, quindi assume che sia definita in un'altra unità di compilazione (= file .cpp) e non restituisce errori. Alla fine della compilazione il linker unisce tutte le unità di compilazione (nel tuo caso il tuo unico file .cpp), ma non trova detta funzione, e quindi restituisce l'errore di "undefined reference"
In soldoni devi definire il costruttore, che assegna i valori di default 1:

Rettangolo::Rettangolo()
{
    base = 1;
    altezza = 1;
}


Un'implementazione migliore si ha usando le liste di inizializzazione, che permettono di inizializzare anche costanti di classe:

Rettangolo::Rettangolo()
:   base(1),
    altezza(1)
{
}


2) La classe ha due coppie di variabili a, b e altezza, base che sono uguali. Ne servono solo due, base e altezza.
3) Le funzioni set dovrebbero avere come tipo di ritorno void, non float (questo risolve i warning: se una funzione ha tipo di ritorno float deve restituire un float, se invece ha tipo di ritorno void non deve restituire nulla).
4) Non sono sicuro di cosa significhi "verificano che entrambi siano numeri in virgola mobile". Un float è un numero in virgola mobile per definizione, non può essere nient'altro. Però ho visto che controlli che base e altezza siano comprese tra 0 e 20, quindi potrebbe essere quello.
5) L'istruzione base=(b >= 0.0 && b < 20.0)? :0; non fa quello che vuoi fare tu. Innanzitutto non è nemmeno un'istruzione valida, anche se è accettata da alcuni compilatori, dovresti mettere un valore dopo il '?'. Quello che fa questa istruzione è:

base=(b >=0.0 && b<20.0)? :0;
// È la versione "sbagliata" di
base=(b >=0.0 && b<20.0)? 1 :0;
// Che è equivalente a
if (b >=0.0 && b<20.0)
    base = 1;
else
    base = 0;


Come vedi a base non viene assegnato il valore di b, ma o 0 o 1. Dovresti invece fare qualcosa del genere:

void Rettangolo::setBase(float b)
{
    if (b >= 0.0 && b < 20.0) {
        base = b;
    }
    // Se b non è compreso tra 0 e 20 lascia il valore precedente.
}


6) Nel main() hai lo stesso problema di prima. Assegni alle variabili altezza e base i valori inseriti dall'utente, ma poi non assegni questi valori all'oggetto r.

int main () {
    Rettangolo r; // Le variabili base, altezza di r hanno il valore assegnato dal costruttore, entrambe 1.
    float base; // Dichiari una variabile base locale alla funzione main()
    float altezza; // Dichiari una variabile altezza locale alla funzione main()
    cout <<"inserisci un valore per la base: ";
    cin >> base; // Assegni alla variabile locale base un valore inserito dall'utente (es. 3)
                 // La variabile base dell'oggetto r ha sempre il valore precedente, 1
    cout <<"inserisci un valore per l'altezza: ";
    cin >> altezza; // Assegni alla variabile locale altezza un valore inserito dall'utente (es. 4)
                    // La variabile altezza dell'oggetto r ha sempre il valore precedente, 1
    cout << "L'area è: " << r.Area() << endl; // Stampa 1, poiché l'oggetto r ha altezza e base uguali a 1
    cout << "Il perimetro è " << r.Perimetro() << endl; // Stampa 4, per lo stesso motivo
    
    // Se invece assegni alle variabili dell'oggetto r i valori inseriti dall'utente
    r.setAltezza(altezza); // Ora la variabile dell'oggetto r ha valore 4
    r.setBase(base); // Ora la variabile dell'oggetto r ha valore 3
    cout << "L'area è: " << r.Area() << endl; // Stampa 12, poiché l'oggetto r ha altezza e base uguali a 3 e 4
    cout << "Il perimetro è " << r.Perimetro() << endl; // Stampa 14, per lo stesso motivo
    return 0;
}

84Valery84
hey grazie mille
mi studio anche questo.
Sto cercando di entrare nel meccanismo anche se un pò a fatica :)
Grazie per il tuo aiuto è davvero molto prezioso e soprattutto per la pazienza :)

84Valery84
Volevo ancora ringraziarti; per alcune cose vado più spedita ora che ho compreso determinati meccanismi. Mi cimenterò in esercizi più complicati così mi esercito e acquisto anche velocità nel programmare. Grazie mille a tutta la comunità e soprattutto a chi ha speso tempo per chiarire le mie incomprensioni.
Thanks :)

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