* näyttö tietämättömäksi jäsenen ominaisuuksista
* "indeksoidut" kentät
Oikeassa ohjelmassa päätesyöttö hoidetaan usein erilaisilta lomakepohjilta. Tällaisen ohjelman tekeminen ensimmäisenä ohjelmana on kuitenkin varsin työlästä. Siispä teemme aluksi suunnitelman mukaisen yksinkertaisen syötön, jossa ainoa korjailumahdollisuus on oletusarvon käyttö. Näppäinten painallusten lukumäärää laskettaessa nyt toteutettava syöttö on aivan yhtä hyvä kuin lomakepohjainen syöttö. Jos pystymme pitämään kaiken laiteriippuvuuden cNaytto- luokassa, on ohjelma kohtuullisella työllä muutettavissa myös lomakepohjaisesti toimivaksi esimerkiksi Windows- käyttöliittymään.
//---------------------------------------------------------------------------- void cNaytto::lisaa_uusi_jasen(char valinta) /* ** Kysyllään uusien jäsenien tietoja ja lisätään jäsenet. ----------------------------------------------------------------------------*/ { cJasen jasen; otsikko(valinta,"Uuden jäsenen lisäys"); while ( 1 ) { jasen.tyhjenna_omat(); jasen.rekisteroi(); cout << endl; cout << "Jäseniä on nyt " << kerho->Jasenia() << "." << endl; cout << "Anna uusi nimi muodossa sukunimi etunimi"; // ei endl int ei_lisata = 1; do { cout << endl; // Jotta uudella kier. rivi vaiht if ( kysy_tiedot(jasen) ) return; cout << "Lisätäänkö" << endl; tulosta(cout,jasen); cout << ":"; if ( kylla_vastaus() ) ei_lisata = ilmoitus(kerho->Jasenet().lisaa(jasen)); if ( ei_lisata ) kerho->poista(jasen.Tunnus_nro()); } while ( ei_lisata ); } }
int cNaytto::kysy_tiedot(cJasen &jasen) { cJasen apujasen(jasen); char jono[50]; cout << "Jäsenen nimi >"; getline(cin,apujasen.nimi,'\n'); if ( apujasen.nimi == "" ) return 1; cout << "Hetu >"; getline(cin,apujasen.hetu,'\n'); ... cout << "Jäsenmaksu mk >"; cin.getline(N_S(jono),'\n'); apujasen.jmaksu = jono_doubleksi(jono,"%lf"); ... jasen = apujasen; return 0; }Tässä on vielä vähän vikoja. Ensinnäkin syötöstä ei voi poistua minkä tahansa kentän antamisen jälkeen. Tämä voidaan korjata esimerkiksi lisäämällä merkin "q" käsittely jokaisen lukemisen jälkeen:
cout << "Jäsenen nimi >"; getline(cin,apujasen.nimi,'\n'); if ( apujasen.nimi == "" ) return 1; if ( apujasen.nimi == "q" ) return 1; cout << "Hetu >"; getline(cin,apujasen.hetu,'\n'); if ( apujasen.hetu == "q" ) return 1; ... cout << "Jäsenmaksu mk >"; cin.getline(N_S(jono),'\n'); if ( strcmp(jono,"q") == 0 ) return 1; apujasen.jmaksu = jono_doubleksi(jono,"%lf"); ...Vieläkään ei ole ehdotettu oletusarvoa kentälle syöttöä varten. Lisäyksessähän tämä ei niin haittaa, mutta jos samaa aliohjelmaa haluttaisiin käyttää korjailussa, olisi oletusarvot toivottavia.
Toimiakseen se, että näyttö voi viitata suoraan jäseneen vaatii näytön jäsenen ystäväksi. No tämähän ei ollut kovin toivottava ominaisuus. Lisäksi olisi toivottavaa muutenkin, ettei näyttö tietäisi näin paljoa jäsenestä. Nyt nimittäin jos jäseneen lisätään yksikin kenttä, pitää muutoksia tehdä monessa paikassa (jo turhan monessa ilman näytönkin muuttamista).
1. Näyttö: montako kenttää sinulla on jäsen? 2. Jäsen: 13 kenttää 3. Näyttö: no annappa minulle 1. kenttä merkkijonona! 4. Jäsen: Ankka Aku 5. Näyttö: Milläs kysymyksellä tämä kenttä kysytään? 6. Jäsen: Jäsenen nimi 7. Näyttö kysyy käytäjältä Jäsenen nimi (Ankka Aku) > => jono 8. Näyttö tutkii vastattiinko q tms. erikoismerkki, jos niin pois 9. Näyttö: Sijoitapa jäsen tämä jono 1. kentäksi. 10. Näyttö jatkaa kohdasta 3 mutta kentälle 2 kunnes kaikki 13 kenttää käsitelty
int cNaytto::kysy_tiedot(cJasen &jasen) { cJasen apujasen(jasen); string jono; int k,kenttia = apujasen.kenttia(), eka = apujasen.eka_kysymys(); for (k=eka; k<kenttia; k++) { jono = apujasen.kentta_jonoksi(k); if ( kysy_kentta(apujasen.kysymys(k),jono) ) return 1; if ( k == eka && jono == "" ) return 1; /* 1. kys pääsee pois pelk. ret * apujasen.sijoita(k,jono); } jasen = apujasen; return 0; }
/**************************************************************************** static int /* * kysy_kentta( /* * const char *viesti ,/* s Viesti joka tulee näytölle * string &jono /* t Jono johon kentän vastaus luetaan. * ) /* ** Funktiolla luetaan vastaus kenttään. ** ** Globaalit: POIS (jono, jolla syöttö katkaistaan) ** Syöttö: päätteeltä ** Tulostus: näyttöön ** Kutsuu: lue_jono_oletus ----------------------------------------------------------------------------* { char apu[80]; int paluu; kopioi_jono(N_S(apu),jono.c_str()); paluu = lue_jono_oletus(viesti,OLET_ALKU,VAKANEN,apu,N_S(apu)); if ( paluu < OLETUS ) return 1; if ( strcmp(apu,POIS) == 0 ) return 1; poista_tyhjat(apu); jono = apu; return 0; }Tulosta ei lueta suoraan muuttujaan jono, jotta mahdollisessa q - vastauksessa ei pilattaisi kentän alkuperäistä arvoa.
class cJasen { ... int kenttia() const { return 13; } int eka_kysymys() const { return 1; } ... };
const string cJasen::kentta_jonoksi(int k) const { switch ( k ) { case 0: return jonoksi(tunnus_nro); case 1: return jonoksi(nimi); case 2: return jonoksi(hetu); case 3: return jonoksi(katuosoite); ... case 10: return jonoksi(jmaksu); case 11: return jonoksi(maksu); case 12: return jonoksi(lisatietoja); default: return string("VIRHE"); } }
const char *cJasen::kysymys(int k) const { switch ( k ) { case 0: return "Tunnus nro"; case 1: return "Jäsenen nimi"; case 2: return "Hetu"; case 3: return "Katuosoite"; ... case 10: return "Jäsenmaksu mk"; case 11: return "Maksettu maksu mk"; case 12: return "Lisätietoja"; default: return "VIRHE!"; } }
int cJasen::sijoita(int k,const string &st) { switch ( k ) { case 0: muunna_jono(st,&tunnus_nro); return 0; case 1: muunna_jono(st,&nimi); return 0; case 2: muunna_jono(st,&hetu); return 0; case 3: muunna_jono(st,&katuosoite); return 0; ... case 9: muunna_jono(st,&liittymisvuosi); return 0; case 10: muunna_jono(st,&jmaksu); return 0; case 11: muunna_jono(st,&maksu); return 0; case 12: muunna_jono(st,&lisatietoja); return 0; default: return 1; }
Itse asiassa sijoita ja kentta_jonoksi sekä kysymys() ovat riittävä määrä jäsenen kysymysten toteuttamiseksi. Näitä metodeja käyttäen voimme samalla muuttaa ison osan aikaisemmin tehtyjä "itseään toistavia" jäsenen metodeja ja muita aliohjelmia silmukoiksi, mm:
ostream &operator<<(ostream &os,const cJasen &jasen) { int k,kenttia=jasen.kenttia(); for (k=0; k<kenttia; k++) os << jasen.kentta_jonoksi(k) << jasen.erotin; return os; } istream &operator>>(istream &is,cJasen &jasen) { int k,kenttia=jasen.kenttia(); for (k=0; k<kenttia; k++) { jasen[k].ota_seuraava(is,jasen.erottimet); } if ( jasen.tunnus_nro >= jasen.seuraava_nro ) jasen.seuraava_nro = jasen.tunnus_nro + 1; tyhjenna(is); // ohittaa loppurivin return is; }