Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: Funktion ylikuormitus (C++)

Azure [16.07.2007 19:28:54]

#

Miten sen nyt sanoisin? Haluaisin oppia ymmärtämään funktion ylikuormittamisen tarpeellisuuden jossain tilanteessa jonkin hyvän esimerkin tai neuvon avulla. C++ ohjelmointia käsittelevässä kirjassani puhuttiin, että sitä voidaan hyödyntää silloin kun haluan esim. kaksinkertaistaa ohjelmaan syötettyn minkä tyyppisen arvon tahansa eli voidaan pelata minkä tyyppisillä arvoilla tahansa ja saada sen tyyppistä arvoa vastaava vastaus tietenkin.

Nyt haluaisn tietää, että miten voin suunnitella ohjelman joka pyyttää tavallaan jonkin arvon (1,1.3 yms) ja kaksinkertaistaisi sen käytäen apuna funktion ylikuormittamista?

Voitte tietenkin keksiä itse paremman esimerkin, että voisin vain oppia käyttämään funktion ylikuormitusta oikein ja ymmärtää sen tarpeellisuuden.

Metabolix [16.07.2007 21:59:08]

#

Ei se aivan noin toimi, kyllä annettavalla datalla pitää olla aina tyyppi. Sen sijaan se toimii niin, että funktioista valitaan oikea annettujen parametrien perusteella, vaikkapa näin:

struct tietoja {
  int korvien_maara;
  double pituus;
  std::string nimi;

  void aseta(int korvat) {
    korvien_maara = korvat;
  }
  void aseta(double metrit) {
    pituus = metrit;
  }
  void aseta(const char *jokin_herja) {
    nimi = jokin_herja;
  }
};

tietoja t;
t.aseta("Matti");
t.aseta(3);
t.aseta(1.25);

std::cout << t.nimi << " on " << t.pituus << " metrin mittainen, ja korvia tuolla kaverilla on " << t.korvien_maara << '.' << std::endl;

Käyttäjän syöttämä teksti tulee luonnollisesti ohjelmaan aina tekstinä, ja sen tunnistaminen kokonaisluvuksi tai liukuluvuksi pitää hoitaa jotenkin muuten. Yksi tapa on kysyä käyttäjältä (kuten yllä), toinen vaihtoehto on lukea se ensin tekstinä ja tarkistaa, onko siinä desimaalierotin, ja jatkaa sen jälkeen hieman kuten yllä eli tunnistuksen perusteella siirtää luku int- tai double-muuttujaan ja käsitellä siitä eteenpäin.

os [17.07.2007 15:19:55]

#

Azure kirjoitti:

... voidaan hyödyntää silloin kun haluan esim. kaksinkertaistaa ohjelmaan syötettyn minkä tyyppisen arvon tahansa eli voidaan pelata minkä tyyppisillä arvoilla tahansa ja saada sen tyyppistä arvoa vastaava vastaus tietenkin.

Tämä onnistuu C++:n mallien avulla:

template <typename Tyyppi> Tyyppi kaksinkertaista(Tyyppi X) { return X*2; }

class HassuLuku {
  private:
     int a;
     float b;

  public:
     HassuLuku(int a0, float b0) : a(a0), b(b0) {}
     HassuLuku operator*(int x) { return HassuLuku(a*x,b-x); }
};

int main() {
  kaksinkertaista(2); // palauttaa kokonaisluvun 4
  kaksinkertaista(1.8); // palauttaa (float)3.6
  kaksinkertaista<double>(1.8); // palauttaa (double)3.6
  kaksinkertaista(HassuLuku(1,1.4)) // palauttaa HassuLuku(2,-0.6);

  return 0;
}

Kääntäjä siis tekee mallista oman funktion kaikille ohjelmassa käytettäville tyypeille, joille mallifunktiossa käytettävät laskutoimitukset, tässä tapauksessa kertolasku kokonaisluvun kanssa, on määritelty.

Azure kirjoitti:

Nyt haluaisn tietää, että miten voin suunnitella ohjelman joka pyyttää tavallaan jonkin arvon (1,1.3 yms) ja kaksinkertaistaisi sen käytäen apuna funktion ylikuormittamista?

Voitte tietenkin keksiä itse paremman esimerkin, että voisin vain oppia käyttämään funktion ylikuormitusta oikein ja ymmärtää sen tarpeellisuuden.

Käyttäjän syöttämien arvojen käsittely ei ole kovin hyvä esimerkki funktioiden ylikuormittamisen tai mallien hyödyistä, koska, kuten Metabolix sanoi, syötetyn datan tyyppi pitää joka tapauksessa tunnistaa itse. Tämän jälkeen ei ole kovin vaikuttavaa, että myös kääntäjä osaa käyttää oikeaa funktiota.

Eräs hyvä käyttötarkoitus ylikuormittamiselle on omasta mielestäni datan vakiomuotoinen tallentaminen esimerkiksi tietokonepelissä:

#include <string>
#include <fstream>
#include <cstring>

struct Vastustaja {
   int x, y;
   std::string nimi;
};

void tallenna(std::ostream &tiedosto, int kokonaisluku) {
   tiedosto.write(&kokonaisluku,sizeof(int));
}
void tallenna(std::ostream &tiedosto, const char *merkkijono) {
   /* tallennetaan myös merkkijonon päättävä nollatavu, jotta merkkijonon
      loppu erottuu tiedostosta */
   tiedosto.write(merkkijono,std::strlen(merkkijono)+1);
}
void tallenna(std::ostream &tiedosto, Vastustaja &vastus) {
   tallenna(tiedosto,vastus.x);
   tallenna(tiedosto,vastus.y);
   tallenna(tiedosto,vastus.nimi.c_str());
}

// + vastaavat lukufunktiot ...

int main() {
  std::ofstream tiedosto("tiedosto.dat",ios_base::binary);
  Vastustaja vastus1 = { 5,6,"Mörkö" };

  tallenna(tiedosto,"Moi");
  tallenna(tiedosto,12345);
  tallenna(tiedosto,vastus1);

  tiedosto.close();
  return 0;
}

Funktioiden ylikuormittaminen eli polymorfismi tekee koodista hieman helpommin kirjoitettavaa: tallenna-funktio tallentaa sille syötetyn datan, ja kääntäjä huolehtii siitä, että kullekin tallennetavalle tietotyypille (int, char[], Vastustaja) kutsutaan funktion oikeaa muotoa.

Vastaus

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

Tietoa sivustosta