A polimorfizmusról általában
  A statikus polimorfizmus

Statikus implicit polimorfizmus:
Generikus programozás

(sablonokon alapuló polimorfizmus)



A generikus programozás statikus implicit polimorfizmust valósít meg és legfontosabb jellemzője a típussal való paraméterezés.

Generikus programozásnál osztályokat és/vagy függvényeket nemcsak értékekkel, hanem egy vagy több típussal is paraméterezünk.

Tudjuk, hogy a polimorfizmus a függvény-mechanizmus általánosításának, kiterjesztésének tekinthető. A generikus programozásra azt mondhatjuk, hogy az a függvények paraméterezésének kiterjesztése.

A 'hagyományos' függvény paramétereinek aktuális értékét a futtató rendszer behelyettesíti a megfelelő formális paraméter helyére, ez tudjuk jól. Vegyük például a következő egyszerű függvényt:

  static int Dupláz(int Mit){
    return 2 * Mit;
  }

Itt a formális paraméter neve Mit és ennél a függvényhívásnál:

  int x;

  x = Dupláz(21);

futás közben a függvény a 21-es értéket kapja meg aktuális paraméterként.

Generikus programozásnál nemcsak értékeket, hanem típusokat is megadhatunk formális paraméterként, amelyek helyére a konkrét hívások helyén nem a futtató rendszer, hanem a fordítóprogram helyettesíti be a konkrét aktuális típust. Ahhoz, hogy megértsük, mit is jelent ez, szükség van egy bemutató mintapéldára.

Bemutató példa: Függvénysablon

A generikus programozással való ismerkedésre a Java-nál alkalmasabb a C++ nyelv, azon belül is legegyszerűbbek a függvény-sablonok. Az explicit polimorfizmusnál bemutatott Java nyelvű
mintapéldát most át fogjuk alakítani úgy C++-ban, hogy függvény-sablont használunk.

Először is lássuk a függvényt, amely a kiszolgáló objektumot paraméterként megkapja és használja. Az eredeti példát egy kicsit módosítjuk, de csak annyiban, hogy nem harcost, hanem egy általános szereplőt fog működtetni a függvény. Ezzel is ki akartuk ugyanis hangsúlyozni, hogy tetszőleges típusú objektumról lehet itt szó (feltéve persze, hogy van nyilvános Vedekezik() metódusa):

  template <typename T>
  void SzereplotMukodtet(T Szereplo){
    Szereplo.Vedekezik();
  }

Ez a függvény-sablon egy típussal (T-vel) van paraméterezve. A paraméter deklarációja ezt mondja:

"Ide tetszőleges típusú paraméter kerülhet, feltéve, hogy rendelkezik minden olyan metódussal, melyet a függvény törzsében felhívok."

Ami most ebben a konkrét esetben azt jelenti, hogy van az illető osztálynak nyilvános Vedekezik() metódusa.

Amikor a fordítóprogram ezt a paraméterezett függvénydeklarációt megtalálja, még nem készíti el a függvényt. Nem is tudná, hiszen már a fordításhoz is szükség van a konkrét paraméter-típusra!

Na de haladjunk tovább a mintapéldával. Legyen egy harcias típusú objektumunk, mely többek között tud védekezni is:

  class Szamuraj{
  public:
    void Vedekezik() { std::cout << "Elszántan kaszabolok!\n"; }
    void HarakiritKovetEl();
    //...
  };

és egy másik, kevésbé félelmetes is:

  class PincsiKutya{
  public:
    void Vedekezik() { std::cout << "Gazdámhoz szaladok!\n"; }
    void Hizeleg();
    //...
  };

Mindkettő alkalmas rá, hogy átadjuk a SzereplotMukodtet függvénynek paraméterként, mert van nyilvános Vedekezik() metódusuk:

  Szamuraj    sz;
  PincsiKutya p;

  SzereplotMukodtet(sz);
  SzereplotMukodtet(p);

Ez a programrészlet nyilván a következőket fogja a képernyőre írni:

  Elszántan kaszabolok!
  Gazdámhoz szaladok!

A két osztály (Szamuraj és PincsiKutya) nem áll semmilyen rokoni kapcsolatban egymással, egyetlen közös vonásuk az, hogy van Vedekezik() metódusuk.



És térjünk vissza most a fordítás folyamatához, hogy jobban megértsük a sablonok működési mechanizmusát! Amikor a fordítóprogram eléri ezt a sort:

SzereplotMukodtet(sz);

akkor látja, hogy szükség van a SzereplotMukodtet függvénynek egy olyan változatára, melynek a paramétere Szamuraj típusú. Ekkor el is készíti ezt:

  void SzereplotMukodtet(Szamuraj Szereplo){
    Szereplo.Vedekezik();
  }

és a megfelelő helyen pedig előállítja a híváshoz szükséges kódot. A másik típusnál természetesen ugyanígy jár el. A dolog lényegéhez tartozik, hogy csak akkor készül el ténylegesen egy-egy konkrét függvény, amikor kiderül, hogy arra szükség van. A sablonokat sütemény-formákhoz szokták hasonlítani: Konkrét sütemény csak akkor készül, amikor valamilyen konkrét Típus-tésztából kivágjuk az előre eltervezett formát.

Azért mondható a generikus programozás a polimorfizmus implicit változatának, mert semmilyen előírás nincs arra, hogy a konkrét típusnak valamely osztály-családhoz kelljen tartoznia. Annak a feltétele, hogy egy adott típus használható legyen egy adott sablonhoz csak annyi, hogy rendelkezzen a ténylegesen meghívandó metódusokkal. Arra nincs megkötés, hogy miféle egyéb tulajdonságokkal rendelkezzen.

Függvénysablon átalakítása függvénytúlterheléssé

Fontos tudni, hogy

A függvénysablonok mindig átírhatók függvénytúlterheléssé, az osztálysablonok pedig típussal nem paraméterezett osztályokká.

Azaz a
fenti példából 'ki tudjuk operálni' a függvénysablont egyszerű függvénytúlterhelés segítségével. A függvénysablont kitöröljük és egyszerűen definiálunk két függvényt a konkrét paramétertípusokkal:

  void SzereplotMukodtet(Szamuraj Szereplo){
    Szereplo.Vedekezik();
  }

  void SzereplotMukodtet(PincsiKutya Szereplo){
    Szereplo.Vedekezik();
  }

és a program többi részét változatlanuk hagyjuk. Az eredmény ugyanaz lesz.

A függvénysablon éppen ezt spórolja meg nekünk: hogy újra és újra leírjuk ugyanazt a függvényt más és más paraméter-típussal. A sablon A sablonok alkalmazásának azért ára is van: hozzá kell szoknunk a kezdetben igen nehezen olvasható szintakszisukhoz. Nagyon sok programozó idegenkedik a sablonoktól a szokatlan szintakszis miatt – még gyakorlott programozók is. És nagyon érdemes legyűrni ezt az idegenkedést! Megéri, mert a sablonok kiváló eszközök.


A statikus polimorfizmus
  A polimorfizmusról általában