PDA

View Full Version : [C++] definizione di una semplice classe


Brazorv
24-06-2005, 19:50
Devo fare un semplice esercizio di c++ che mi chiede di implementare la classe Complex.
ho fatto questi due file
complex.h

#ifndef COMPLEX_H
#define COMPLEX_H

class Complex{

public:
Complex(float=0.0, float=0.0);
void setRe(float);
float getRe();
void setIm(float);
float getIm();
void print();

private:
float re; // parte reale
float im; // parte immaginaria
}


#endif


e complex.cpp

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

Complex::Complex(float r, float i){
setRe(r);
setIm(i);
}

void Complex::setRe(float r){
re = r;
}

void Complex::setIm(float i){
im = i;
}

float Complex::getRe(){
return re;
}

float Complex::getIm(){
return im;
}

void Complex::print(){
std::cout << "(" << re << "," << im << ")" ;
}



quando complilo mi da il seguente errore

complex.cpp:4: error: new types may not be defined in a return type
complex.cpp:4: error: return type specification for constructor invalid

dove sbaglio?

jappilas
24-06-2005, 20:09
scusa ma c'è una cosa che non mi torna:
tu chiami funzioni membri della classe per implementare il costruttore...mentre se non sbaglio le funzioni Set e Get dovrebbero essere attive dopo che l' oggetto è stato istanziato, non prima... e infatti tutti i costruttori che ho visto erano della forma
dato1(a); dato2(b)... ; in questo caso re(r);im(i); ...

Brazorv
24-06-2005, 20:21
scusa ma c'è una cosa che non mi torna:
tu chiami funzioni membri della classe per implementare il costruttore...mentre se non sbaglio le funzioni Set e Get dovrebbero essere attive dopo che l' oggetto è stato istanziato, non prima... e infatti tutti i costruttori che ho visto erano della forma
dato1(a); dato2(b)... ; in questo caso re(r);im(i); ...

nel libro che sto usando, il Deitel, usa le set nel costruttore della classe, serve per non scrivere codice duplicato, perchè sia nel costruttore che nelle set si dovrebbero fare dei controlli sulla validità dei parametri.

cmq ho ricompilato senza usare le set nel costruttore e mi da lo stesso problema. :boh:

cionci
24-06-2005, 20:54
C'era una volta un punto e virgola.
Il punto e virgola si sentiva minacciato
dalla parentesi chiusa e decise quindi
di lasciare la sua posizione e di andare
a cercar fortuna per il mondo. :D

Brazorv
24-06-2005, 20:59
C'era una volta un punto e virgola.
Il punto e virgola si sentiva minacciato
dalla parentesi chiusa e decise quindi
di lasciare la sua posizione e di andare
a cercar fortuna per il mondo. :D

penso che questo errore me lo ricorderò ogni volta che farò una nuova classe :D

fek
24-06-2005, 21:06
penso che questo errore me lo ricorderò ogni volta che farò una nuova classe :D

E lo sbaglierai ogni volta come io lo sbaglio ogni volta da piu' di 10 anni a questa parte :D

Brazorv
24-06-2005, 21:12
E lo sbaglierai ogni volta come io lo sbaglio ogni volta da piu' di 10 anni a questa parte :D

sicuramente
ma almeno la prossima volta non rimarrò un'ora a capire casa ho sbagliato :grrr:
spero :sperem:

anx721
24-06-2005, 21:28
E lo sbaglierai ogni volta come io lo sbaglio ogni volta da piu' di 10 anni a questa parte :D


e non capisco a che diavolo serva poi quel punto e virgola oltre a farti sbagliare ogni volta...è un esempio di ridondanza sintattica veramente inutile... :mad:

Brazorv
25-06-2005, 12:02
Continuando l'esercizio ho scritto 2 metodi per fare la somma e la differenza

Complex Complex::add(const Complex *b) const{
Complex res;
res.setRe(re + b->getRe());
res.setIm(im + b->getIm());
return res;
}

Complex Complex::subtract(const Complex &b) const{
Complex res;
res.setRe(re - b.getRe());
res.setIm(im - b.getIm());
return res;
}


e vongono chiamati così

c=a.add(&b);
c=a.subtract(b);

secondo voi è meglio usare i puntatori come in add o i riferimenti come in subtract?

cionci
25-06-2005, 13:19
e non capisco a che diavolo serva poi quel punto e virgola oltre a farti sbagliare ogni volta...è un esempio di ridondanza sintattica veramente inutile... :mad:
Non è vero... Quella oltra ad una dichiarazione può anche essere uno statement (anzi lo è)...per questo serve il ;

class a {
...
} pippo;

cionci
25-06-2005, 13:21
Continuando l'esercizio ho scritto 2 metodi per fare la somma e la differenza
secondo voi è meglio usare i puntatori come in add o i riferimenti come in subtract?
Secondo te quale si capisce meglio ;)
Se poi consideri che in questo caso dal puto di vista della traduzione in assembly sono esattamente equivalenti cosa preferisci ?

jappilas
25-06-2005, 14:04
non facevi prima a ridefinirti gli operatori? ;)

fek
25-06-2005, 14:39
Continuando l'esercizio ho scritto 2 metodi per fare la somma e la differenza

Complex Complex::add(const Complex *b) const{
Complex res;
res.setRe(re + b->getRe());
res.setIm(im + b->getIm());
return res;
}

Complex Complex::subtract(const Complex &b) const{
Complex res;
res.setRe(re - b.getRe());
res.setIm(im - b.getIm());
return res;
}


e vongono chiamati così

c=a.add(&b);
c=a.subtract(b);

secondo voi è meglio usare i puntatori come in add o i riferimenti come in subtract?

La consuetudine vuole che i parametri di ingresso si passino sempre con const&, mentre i parametri in uscita come puntatori.

Brazorv
25-06-2005, 15:19
non facevi prima a ridefinirti gli operatori? ;)
sicuramente è il modo migliore ma ancora non ci sono arrivato


La consuetudine vuole che i parametri di ingresso si passino sempre con const&, mentre i parametri in uscita come puntatori.


allora è meglio così


Complex *Complex::subtract(const Complex &b) const{
Complex *res;
res->setRe(re - b.getRe());
res->setIm(im - b.getIm());
return res;
}


Grazie

cionci
25-06-2005, 15:24
Complex *Complex::subtract(const Complex &b) const{
Complex *res;
res->setRe(re - b.getRe());
res->setIm(im - b.getIm());
return res;
}


Grazie
Questo codice è sbagliato...res non è allocato...e comunque non puoi ritornare un puntatore lì, a meno che tu non faccia un'allocazioen dinamica...

Brazorv
25-06-2005, 15:31
Questo codice è sbagliato...res non è allocato...e comunque non puoi ritornare un puntatore lì, a meno che tu non faccia un'allocazioen dinamica...
si è vero non l'avevo provato
questo invece dovrebbe andare bene

Complex *Complex::subtract(const Complex &b) const{
Complex *res = new Complex();
res->setRe(re - b.getRe());
res->setIm(im - b.getIm());
return res;
}

jappilas
25-06-2005, 15:58
sicuramente è il modo migliore ma ancora non ci sono arrivato

è come per le funzioni ... ;) in questo caso :Complex operator+( Complex &altro );
Complex operator-( Complex &altro ); come membri pubblici della classe, e
Complex Complex::operator+( Complex & altro ){
return Complex( re + altro.re, im + altro.im );
};

Complex Complex::operator-( Complex & altro ){
return Complex( re - altro.re, im - altro.im );
};

come implementazione,
in questo caso effettivamente crea un nuovo oggetto Complex e lo ritorna ;)

jappilas
25-06-2005, 16:03
si è vero non l'avevo provato
questo invece dovrebbe andare bene

Complex *Complex::subtract(const Complex &b) const{
Complex *res = new Complex();
res->setRe(re - b.getRe());
res->setIm(im - b.getIm());
return res;
}


uhm, crei un nuovo oggetto per usarne il puntatore, ma così mi pare resti allocato anche quando vai fuori scope
al che chiedo (perchè ho il dubbio, non è un dom retorica) : non è codice a rischio di leak? ;)

stavo dando un' occhiata al criterio RAII (http://www.hackcraft.net/raii/), e mi pare la metodica sia differente...

Brazorv
25-06-2005, 16:09
uhm, crei un nuovo oggetto per usarne il puntatore, ma così mi pare resti allocato anche quando vai fuori scope
al che chiedo (perchè ho il dubbio, non è un dom retorica) : non è codice a rischio di leak? ;)
ecco sono queste le cose che sto cercando di capire
però in effetti io volgio creare un nuovo oggetto che è il risulatato di una operazione fara altri 2.
Se metto un messaggio dentro il distruttore dell'oggetto riesco a capire quando viene distrutto?

fek
25-06-2005, 16:10
si è vero non l'avevo provato
questo invece dovrebbe andare bene

Complex *Complex::subtract(const Complex &b) const{
Complex *res = new Complex();
res->setRe(re - b.getRe());
res->setIm(im - b.getIm());
return res;
}


Questa implementazione viola un principio importante che va sempre tenuto a mente quando si programma in C++:
"Resource Acquisition Is Initalisation"

In italiano significa che chi crea un oggetto ne prende anche possesso ed e' il responsabile della sua distruzione. Se crei un oggetto Complex all'interno di un altro oggetto Complex dovresti anche mettere a disposizione i servizi per la sua distruzione.

Prova questa implementazione:

Complex& Complex::subtract(const Complex &b) const{
setRe(re - b.getRe());
setIm(im - b.getIm());
return *this;
}


Se non vuoi che l'operazione modifichi l'oggetto, prova questa:


Complex& Complex::subtract(Complex* result, Complex const& a, Complex const& b) const
{
result->setRe(a.getRe() - b.getRe());
result->setIm(b.getIm() - b.getIm());
return *res;
}


Questa versione segue la convenzione matematica di mettere il risultato come primo argomento. La convenzione in C++ lo vorrebbe pero' passato come ultimo argomento. Gusto personale qui.

cionci
25-06-2005, 16:10
non è codice a rischio di leak? ;)
Mooooolto...

jappilas
25-06-2005, 16:16
Questa implementazione viola un principio importante che va sempre tenuto a mente quando si programma in C++:
"Resource Acquisition Is Initalisation"

che è appunto questo http://www.hackcraft.net/raii/ allora il mio dubbio di prima è confermato :)
[IMPORTANTE]
In italiano significa che chi crea un oggetto ne prende anche possesso ed e' il responsabile della sua distruzione.
[/ IMPORTANTE]
Se crei un oggetto Complex all'interno di un altro oggetto Complex dovresti anche mettere a disposizione i servizi per la sua distruzione.

<implementazioni a e b> ...

Questa versione segue la convenzione matematica di mettere il risultato come primo argomento. La convenzione in C++ lo vorrebbe pero' passato come ultimo argomento. Gusto personale qui.
infatti... anche se continuo ad avere l' impressione che l' operator overloading introduca meno overhead.. o sbaglio? :D

cionci
25-06-2005, 16:16
E questa ? Certo è un po' sprecona, ma viola qualche principio ?

Complex Complex::subtract(Complex const& a, Complex const& b) const
{
Complex result;
result.setRe(a.getRe() - b.getRe());
result.setIm(b.getIm() - b.getIm());
return result;
}

cionci
25-06-2005, 16:20
infatti... anche se continuo ad avere l' impressione che l' operator overloading introduca meno overhead.. o sbaglio? :D
Che io sappia l'overloading degli operatori aritmetici e di stream è tendenzialmente poco usato...probabilmente per una questione di chiarezza della classe...

fek
25-06-2005, 16:24
infatti... anche se continuo ad avere l' impressione che l' operator overloading introduca meno overhead.. o sbaglio? :D

Mi associo a cionci, non vado d'accordo con l'overloading degli operatori :)

Ho visto cose agghiaccianti come questa:



List aList;

!List;


Ed ora dimmi l'operatore ! che operazione esegue sulla lista senza andare a guardarne l'implementazione.

L'overloading degli operatori poi hanno regole di casting implciti. precedenze e creazione di oggetti temporanei non proprio intuitive ed e' facile sbagliarsi e spesso non e' banale capire quello che succede solo a guardare il codice.
Quindi in genere cerco di evitarli e di scrivere codice che si autodocumenta anche se un po' piu' "prolisso".

Va detto pero' che nel caso dei numeri complessi l'operatore "+" e' autoesplicativo e non puo' dar adito ad alcuna confusione, quindi ci sta. In questo caso diventa quasi una questione di stile personale.

fek
25-06-2005, 16:27
E questa ? Certo è un po' sprecona, ma viola qualche principio ?

Complex Complex::subtract(Complex const& a, Complex const& b) const
{
Complex result;
result.setRe(a.getRe() - b.getRe());
result.setIm(b.getIm() - b.getIm());
return result;
}


Alcuni compilatori sono in grado (date certe regole) di trasformarla automaticamente nella versione che ho postato con il passaggio esplicito dell'oggetto risultato. Se non lo fanno, allora viene automaticamente creata una variabile temporanea e c'e' un costruttore di copia di mezzo ed un distruttore di mezzo.

Se semplifica il codice ci sta e ben venga. Se ti accorgi che gli oggetti temporanei diventano un collo di bottiglia da qualche parte, fai sempre in tempo ad aiutare il compilatore con la mia versione.

jappilas
25-06-2005, 16:34
Mi associo a cionci, non vado d'accordo con l'overloading degli operatori :)

Ho visto cose agghiaccianti come questa:



List aList;

!List;


Ed ora dimmi l'operatore ! che operazione esegue sulla lista senza andare a guardarne l'implementazione.

L'overloading degli operatori poi hanno regole di casting implciti. precedenze e creazione di oggetti temporanei non proprio intuitive ed e' facile sbagliarsi e spesso non e' banale capire quello che succede solo a guardare il codice.
Quindi in genere cerco di evitarli e di scrivere codice che si autodocumenta anche se un po' piu' "prolisso".
ehm, sì effettivamente è meglio la comprensibilità

Va detto pero' che nel caso dei numeri complessi l'operatore "+" e' autoesplicativo e non puo' dar adito ad alcuna confusione, quindi ci sta. In questo caso diventa quasi una questione di stile personale.
sguap... -_-'
ehm , riconosco che come mio solito ragionavo per compartimenti stagni e non andavo al di là del caso specifico di poco fa... (quindi operatori A-L, +-*/<> e pochi altri)
in genere, hai ragione, meglio adottare linee guida di programmazione che massimizzino la "straigth - correctness" e comprensibilità del codice, anche in previsione un altro ci debba mettere le mani in secondo tempo) e cose come !aList di certo son tutto meno che autoesplicative... :mbe:

fek
25-06-2005, 16:41
in genere, hai ragione, meglio adottare linee guida di programmazione che massimizzino la "straigth - correctness" e comprensibilità del codice, anche in previsione un altro ci debba mettere le mani in secondo tempo) e la !aList di certo è tutto meno che autoesplicativa... :mbe:

Un attimo, il tuo ragionamento non e' sbagliato nel caso dei Complex, soprattutto se semplifica il codice e magari lo rende anche piu' leggibile nel caso di oggetti che sono intrinsecamente numeri.

Sutter consiglia nel caso degli operatori "Do as the ints do", cioe' di fare come fanno gli interi. Se l'implementazione naturale di operatore "+" per una tua classe e' esattamente quello che tutti si aspetterebbero, va benissimo implementarlo.

La cosa da tenere a mente secondo me e' di non abusarne, cosa che vale un po' per tutti i costrutti del C++.

Brazorv
25-06-2005, 16:43
Complex& Complex::subtract(Complex* result, Complex const& a, Complex const& b) const
{
result->setRe(a.getRe() - b.getRe());
result->setIm(b.getIm() - b.getIm());
return *res;
}
questa è la seconda soluzione che ha postato fek
mi viene il dubbio che siccome ho passato un puntatore come primo argomento il risultato si troverà automaticamente in *result, come in C perciò questa mi sembra uguale

void Complex::subtract(Complex* result, Complex const& a, Complex const& b) const
{
result->setRe(a.getRe() - b.getRe());
result->setIm(b.getIm() - b.getIm());
}

o no?

fek
25-06-2005, 16:47
questa è la seconda soluzione che ha postato fek
mi viene il dubbio che siccome ho passato un puntatore come primo argomento il risultato si troverà automaticamente in *result, come in C perciò questa mi sembra uguale
o no?

Si' le due versioni sono quasi esattamente equivalenti.

Nel secondo caso ritorni anche una reference al risultato e la cosa ti permette di concatenare la chiamata alla funzione con una chiamata ad un metodo della classe Complex, ad esempio cosi':


Complex result;
float realPart = subtract (&result, a, b).getRe();


che e' piu' concisa ed e' equivalente a:


Complex result;
subtract (&result, a, b);;

float realPart = result.getRe();


Se pensi che questo genere di costrutto non ti servira' mai oppure rende il tuo codice piu' complesso del necessario, usa pure la versione con void che va benissimo. Caso mai ti servisse, fai sempre in tempo a modificare il metodo per tornare il risultato come reference.

Brazorv
25-06-2005, 16:54
Se pensi che questo genere di costrutto non ti servira' mai oppure rende il tuo codice piu' complesso del necessario, usa pure la versione con void che va benissimo. Caso mai ti servisse, fai sempre in tempo a modificare il metodo per tornare il risultato come reference.
si in effetti può essere utile.
adesso cerco qualcos'altro da chiedervi :D

fek
25-06-2005, 16:57
si in effetti può essere utile.
adesso cerco qualcos'altro da chiedervi :D

Non scrivere qualcosa perche' puo' esserti utile, scrivila solo se effettivamente ti serve :)

ghiotto86
25-06-2005, 18:33
e non capisco a che diavolo serva poi quel punto e virgola oltre a farti sbagliare ogni volta...è un esempio di ridondanza sintattica veramente inutile... :mad:

è come se facessi un typedef , definisci un nuovo tipo.

la classe è solo un interfaccia complessa che definisce dati e istruzioni e quindi come in ogni cosa nel c++ di questo tipo va col ;

io quando inizio a crare una classe prima che dimentichi quel famigerato ;

faccio

class X{};

e via :D

ghiotto86
25-06-2005, 18:34
Non scrivere qualcosa perche' puo' esserti utile, scrivila solo se effettivamente ti serve :)
mentalità RISC :sofico:

cionci
25-06-2005, 19:01
io quando inizio a crare una classe prima che dimentichi quel famigerato ;

faccio

class X{};

e via :D
Anche io :)

Brazorv
25-06-2005, 19:03
Ho fatto delle prove per vedere quando vengono creati gli oggetti e quando vengono distrutti. Ho aggiunto delle istruzioni per stampare dei messaggi nel costruttore e nel distruttore. nella main creo 3 oggetti, poi ad uno di questi gli assegno la somma degli altri 2.
Con la soluzione di cionci mi ha dato questo:
(1,1)Creato
(0,0)Creato
(2,2)Creato
(1,1)
(2,2)
(0,0)Creato
(3,4)Distrutto
(3,4)
(2,2)Distrutto
(3,4)Distrutto
(1,1)Distrutto
l'oggetto temporaneo viene effettivamente creato
Con l'overloading degli operatori:
(1,1)Creato
(0,0)Creato
(2,2)Creato
(1,1)
(2,2)
(3,3)Creato
(3,3)Distrutto
(3,3)
(2,2)Distrutto
(3,3)Distrutto
(1,1)Distrutto
anche qui c'è loggetto temporaneo
Con la funzione di fek:
(1,1)Creato
(0,0)Creato
(2,2)Creato
(1,1)
(2,2)
(3,3)
(2,2)Distrutto
(3,3)Distrutto
(1,1)Distrutto
c'e un oggetto in meno.

Brazorv
25-06-2005, 19:07
io quando inizio a crare una classe prima che dimentichi quel famigerato ;

faccio

class X{};

e via

io di solito quando apro una parentesi graffa la chiudo sempre. Seguirò il tuo metodo per le classi.

fek
25-06-2005, 19:37
c'e un oggetto in meno.

Hai fatto una cosa furba, hai provato empiricamente che una versione e' piu' veloce di un'altra.

Ora pero', la scelta su quale delle due usare dipende per prima cosa dalla leggibilita' del tuo codice: scegli la versione che rende il tuo codice piu' semplice e chiaro da leggere, ignora per ora la velocita' di esecuzione.

Fenomeno85
25-06-2005, 20:17
C'era una volta un punto e virgola.
Il punto e virgola si sentiva minacciato
dalla parentesi chiusa e decise quindi
di lasciare la sua posizione e di andare
a cercar fortuna per il mondo. :D

bellissima questa :D :asd: .... me la segno giù :D


~§~ Sempre E Solo Lei ~§~