erlug
[Top] [All Lists]

Re: [Erlug] c e classi

To: erlug@xxxxxxxxxxxxxx
Subject: Re: [Erlug] c e classi
From: Davide Bolcioni <db_erlug@xxxxxxxx>
Date: Tue, 19 Aug 2003 23:17:35 +0200
Simone Bacciglieri ha scritto:

perchè il listato di prova allegato mi produce questo output?
-----------------------------------------------------------------------------------
Creo punto

Distruggo punto

x= 0
y= 0


Distruggo punto
----------------------------------------------------------------------------------

E poi: cosa devo usare per poter stampare a video una stringa partendo da colonna x e riga y?

Dipende dalla piattaforma: su Unix, [n]curses. Per un quick hack, vedi
le sequenze di escape VT100 - a console funzionano (e qualche volta anche in emulatori di terminale come putty, ma se si comincia a porsi
questo problema conviene passare a [n]curses).

------------------------------------------------------------------------

#include <iostream>

using namespace std;

class punto {
        public:
                punto();
                ~punto();

Qui non hai esplicitato il costruttore di copia (copy constructor,
abbreviato copy ctor): il compilatore ne fornisce uno di default che
nel tuo caso (probabilmente per caso) si comporta in modo adeguato.

                punto(const punto& other): x(other.x), y(other.y) { }

                void impostapunto(int,int);
                void printpunto(void);
        private:
                int x;
                int y;
};

punto::punto()
{
        cout << "\nCreo punto\n";

Sarebbe meglio:
  punto::punto(): x(0), y(0)
  {
        cerr << "\nCreo punto\n";
  }
        x=0;
        y=0;
}

punto::~punto()
{
        cout << "\nDistruggo punto\n";
        cerr << "\nDistruggo punto\n";
}
void punto::impostapunto(int a,int b)
{
        x=a;
        y=b;
}

Cosa ti ha fatto di male l'operatore di assegnamento ?

void punto::printpunto(void)
{
        cout << "\nx= " << x << "\ny= " << y << "\n\n";
}

Cosa ti ha fatto di male << ?

class usa {
        public:
                void usapunto(punto);
};

Ecco il tuo problema ...

void usa::usapunto(punto p)
{
        int y=10;
        int x=11;
        p.impostapunto(y,x);
}

La funzione usapunto() dichiara il proprio argomento *per valore*, il che significa
che:
- viene fatta una copia del parametro nell'argomento p, usando il
  costruttore di copia;
- tale copia p è locale alla funzione;
- x e y sono impostati (scambiati ?) in p;
- p viene distrutto in uscita dalla funzione.

main()
{
        punto pu;
        usa us;
        us.usapunto(pu);
        pu.printpunto();
}

Qui pu ha x = 0 e y = 0, usapunto() come sopra indicato non ne cambia i
valori e printpunto() stampa appunto 0 e 0. Temo che tu soffra di sindrome da Visual Basic, è l'unico linguaggio che mi venga in mente a
passare per riferimento i parametri. Visual Basic ha rovinato parecchia
brava gente, ma si può rimediare se viene curato per tempo :->

Ti propongo quanto segue:

#include <iostream>

// Non è buon stile usare 'using namespace std;' ...

class punto {
public:
        punto(): x(0), y(0) { } // Nota 1
        punto(int xx, int yy): x(xx), y(yy) { } // Nota 2
        punto(const punto& other): x(other.x), y(other.y) { } // Nota 3
        ~punto() { } // Nota 4

        punto& operator= (const punto& p) { // Nota 5
          if (this != &p) { // Nota 6
            x = other.x;
            y = other.y;
          }

          return *this;
        }

        // Nota 7
private:
        int x;
        int y;
}

Note:
[1] la notazione x(0) è una buona abitudine, si chiama 'member initizaliation syntax', è sempre più efficiente che scrivere x=0 nel
corpo del costruttore (vale per i costruttori) altrimenti C++ prima
inizializza x a un valore di default (int è un caso speciale e viene
inizializzato a ... quel che c'è in RAM) e poi eseguendo x = 0 ci passa
sopra.
[2] questo è un costruttore che serve a creare punti diversi da (0, 0)
senza dover fare l'inefficiente giro sopra indicato (prima li creo e
subito dopo li imposto).
[3] questo è il costruttore di copia, viene usato quando si crea una
istanza di "punto" per copia di una esistente.
[4] c'è una regola empirica detta 'rule of three': se hai bisogno di un
costruttore di copia o di un operatore di assegnamento o di un
distruttore, allora con tutta probabilità hai bisogno di tutti e
tre. Nel caso in esame tecnicamente potresti fare a meno di tutti a tre,
ma mi è parso il caso di sfruttare la semplicità dell'esempio.
[5] questo è l'operatore di assegnamento e fa le veci di impostapunto(),
nel senso che consente di scrivere

  p = punto(11, 17);

senza perdita di efficienza.
[6] quando si scrive l'operatore di assegnamento, occorre chiedersi se
per caso non ci si sta scrivendo addosso (nei costruttori non può succedere, creano un oggetto nuovo); ancora una volta nel caso in esame
non succederebbe niente, ma in generale il problema bisogna porselo e
questo è un modo di far sapere a chi legge il codice che me lo sono
posto. Tecnicamente questo approccio non è correttissimo per questioni
legate alle eccezioni, ma mi pare il caso di affrontare una cosa alla
volta.
[7] ora devo uscire, rimando a domani un piccolo esempio di definizione
di operator<< che consentirà di scrivere:

  cout << p;

per l'output di punti.

Per venire al problema, in barba a chi mi accusa di essere troppo
conciso, bisogna sapere cosa si intende ottenere. Se usapunto() *deve*
modificare il punto (pratica sconsigliatissima, mi riprometto di
tornarci sopra), basta:

  usapunto(punto& p) {
  ...
  }

altrimenti usapunto() non ti serve e ciò di cui hai bisogno è
il costruttore

  punto p(11, 17);

o l'operatore di assegnamento

  p = punto(11, 17);

Infine mi scuso per non aver avuto il tempo di provare quanto sopra
al compilatore -mi riprometto un seguito.

Davide Bolcioni
--
Linux - the choice of a GNU generation.


<Prev in Thread] Current Thread [Next in Thread>