Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: C++: try/catch/throw

mandariini [29.11.2020 14:53:31]

#

Hei,
Koitin tavata ohjetta kys. aiheesta ja googlasin lisääkin, mutta kaipaisin vielä vähän rautalangasta vääntämistä.

Minulla on siis tämmöinen koodi:

alku:
	cout << "Kuinka monta tietoa haluat syöttää (max. 10)?: ";
	cin >> count;
	try {
		if (count > 1 || count < 10)
			throw(count);
	 }
	catch (int b){
		cout << "Luku " << b << " on liian pieni.";

	goto alku;
	}

Tällä haluaisin estää sen, ettei käyttäjä syötä muita lukuja 1-10 ulkopuolelta. Tämä toimii ensimmäisen kerran ihan hyvin, mutta nyt on joku häikkä, kun vaikka syötän "oikaen" luvun, niin jatkaa silti herjaamista "Luku on liian pieni".

peran [29.11.2020 15:31:39]

#

jos luku on pienempi kuin 10. Esim 0, niin heittää poikkruksen.
Jos luku on suurempi kuin 1. Esim 15, niin heittää poikkeuksen.
Jos luku onpienempi kuin 10. Esim. 5, niin heittää poikkeuksen.

Edit - siis heittää kaikilla numeroilla poikkeuksen.

mandariini [29.11.2020 16:43:28]

#

Äh totta! Miten en tätä ajatellut. Miten tuo olisi sitten järkevin toteuttaa?
Jaoin nuo kahteen eri ifiin mutta ei auttanut.

Metabolix [29.11.2020 16:57:23]

#

Järkevää olisi tietenkin laittaa ne vertailut oikein päin. Mieti nyt, mitä olet ehtoon kirjoittanut: jos luku on suurempi kuin yksi, onko se virhe?

Vika ei sinänsä ole catch-rakenteessa, mutta tässä tilanteessa olisi järkevintä jättää kyseinen rakenne pois ja laittaa suoraan if-lauseeseen virheilmoitus ja alkuun palaaminen.

mandariini [29.11.2020 17:06:05]

#

Sen korjasin myös, ajatusvirhe. Onko se "hyvää ohjelmointia" toteuttaa vain whilellä? Näinhän se lähti toimimaan:

while (count > 20 || count < 1) {
		cout << "Wrong number, try again. \n";
		cin >> count;
	}

Mietin myös, kun koodissani on useampi kohta johon tämä pitäisi laittaa, niin onko hyvän koodaamisen/käytettävyyden kannalta lisätä näitä whilejä vain jokaiseen kohtaan missä sitä tarvitaan?

Jatkokysymys: Kuinka luoda tarkistus, jos käyttäjä yrittää syöttää kirjaimen numreon sijaan? Ja toisinpäin? Esim tässä jos käyttäjä syöttää kirjaimia numeroiden sijaan. Nyt jos käyttäjä tekee tämän virheen niin huonosti käy. :D

eq [29.11.2020 18:40:31]

#

Muotoillun syötteen lukemisen onnistumista voi seurata tarkkailemalla cin-objektin tilaa lukuoperaation jälkeen. Esimerkki yleisellä tasolla

#include <iostream>

int main()
{
  int num;
  std::cin >> num;
  if (std::cin.good()) {
    // luettiin onnistuneesti jokin kokonaisluku muuttujaan num ilman,
    // että päädyttiin cin-virran loppuun (ts. virrasta löytyi luvun jälkeen
    // vielä muu merkki, esimerkiksi rivinvaihto, johon lukeminen lopetettiin)
  } else {
    // *mahdollinen* lukuvirhe
    if (std::cin.bad()) {
      // cin-virta on rikki, sieltä ei kannata odottaa lisää syötteitä
      // jos ohjelma ei voi jatkaa ilman syötettä, niin tämä on hyvä hetki
      // antaa esim. virheilmoitus ja sammua
    } else if (std::cin.eof()) {
      // cin-virta luettu loppuun asti - tässäkin tapauksessa virrasta on
      // turha enää yrittää lukea lisää
      if (!std::cin.fail()) {
        // luettiin kokonaisluku, mutta virta loppui kesken sitä lukiessa
        // saatiinko koko luku? cin-virran tapauksessa vastaus on varma ehkä,
        // jossain muussa virrassa ehkä ei
      } else {
        // virta loppui kesken ennen kuin sieltä löytyi mitään luvuksi tulkittavaa
      }
    } else {
      // virrassa ensimmäisenä oleva syöte ei ole tulkittavissa luvuksi
      // voi johtua esim. siitä, että käyttäjä kirjoitti luvun sijaan kirjaimen
      // virran virhetilan voi nollata clear-funktiolla
      //std::cin.clear();
      // jonka jälkeen virrasta voi ohittaa esimerkiksi seuraavan rivin
      //std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
      // jonka jälkeen käyttäjältä voi yrittää kysyä lukua uudelleen
    }
  }
}

EDIT! Huom. Alkuperäisessä esimerkissä oli pieni (iso) virhe – ei pitäisi koskaan viilata näitä koodiesimerkkejä liikaa. if (cin) ei tietenkään ole synonyymi ehdolle if (cin.good()), vaan ehdolle if (!cin.fail()). Jälkimmäinen on tietysti siitä joustavampi ehto, että se ei mene epätodeksi, vain siksi, että virta loppuu kesken (kunhan viimeisin lukuoperaatio onnistuu) – vaan vasta sitten, kun loppuneesta virrasta yritetään lukea lisää. Yleensä käytetäänkin muotoa if (cin) – alla tiivistetympi esimerkki tyypillisemmästä tapauksesta (toivottavasti menee oikein testaamatta):

int num;
std::cin >> num;
if (std::cin) {
  // luettiin luku muuttujaan onnistuneesti
} else {
  if (std::cin.bad() || std::cin.eof()) {
    // lukeminen ei onnistunut, eikä sitä kannata yrittää uudelleen
  } else {
    // virrassa on jotain muuta kuin luku; voimme yrittää jättää sen huomiotta
    // ja yrittää uudelleen kuten ylempänä
  }
}

Metabolix [29.11.2020 19:23:56]

#

mandariini kirjoitti:

Mietin myös, kun koodissani on useampi kohta johon tämä pitäisi laittaa, niin onko hyvän koodaamisen/käytettävyyden kannalta lisätä näitä whilejä vain jokaiseen kohtaan missä sitä tarvitaan?

Kyllä ja ei: Syötteen tarkastus ei tapahdu itsestään, joten joudut kyllä tarkastamaan sen jokaisessa kohdassa. Silti tarkastusta ei ehkä kannata kopioida joka paikkaan, vaan toistuvista asioista kannattaa tehdä funktioita. Esimerkiksi voi tehdä funktion lue_luku, joka näyttää kysymyksen, lukee syötteen ja tarkastaa lukuvälin.

count = lue_luku("Montako tietoa haluat syöttää?", 1, 10);

Toisaalta jos (aloittelijan) ohjelmassa toistuu aivan täsmälleen sama asia monta kertaa, usein ohjelma on jotenkin huonosti suunniteltu. Jos siis vaikka samaan count-muuttujaan luetaan samalla tavalla arvo monessa eri kohdassa ohjelmaa, on syytä miettiä, onko ohjelma kokonaisuutena järkevästi tehty.

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta